Java基础教程笔记
第一部分——java基础程序设计
一:java语言特色
1:语言有点:“一次编写,到处运行”
2:相对于C++
A:提供了对内存的自动管理;B:去除了C++语言中的“指针”;C:避免了赋值语句(如a=3)于逻辑运算语句(如a==3)的混淆;D:取消了多重继承
Java是一种解释型语言,所以它的执行效率相对会慢一点,但是Java语言采用了两种手段,使其性能还是不错的
A:Java语言源程序编写完成后,先使用java伪编译器进行伪编译,将其转换为中间码(也称字节码),再解释;
B:提供了一种“准实时”(Just-in-Time,JIT)编译器,当需要更快的速度时,可以使用JIT编译器将字节码转换成机器码,然后将其缓冲下来,这样速度就会更快
C:健壮性——在伪编译时,做了许多早期潜在问题的检查,并且在运行时又做了一些相应的检查
3:java虚拟机
图中可看出Java可以实现可移植性的原因,只要在操作系统上植入JVM(java虚拟机),java程序就具有可移植性
4:Java技术架构
A:J2EE(Java 2 Platform Enterprise Edition)企业版,是以企业为环境而开发应用程序的解决方案
B:J2SE(Java 2 Platform Stand Edition)标准版,是桌面开发和低端商务应用的解决方案
C:J2ME(Java 2 Plataform Micro Edition)小型版,是致力于消费产品和嵌入式设备的最佳解决方案
5:claspath的指定
在java中可以使用set classpath命令指定java类的执行路径。
二——简单的java程序
1:如果将一个类声明成public,则也要讲文件名取成和这个类一样的名称。也就是说,在一个java文件里,最多只能一个public类
2:println——由print和line组成
3:变量是利用声明的方式,将内存中的某个块保留下来以供程序使用
4:java的变量类型
5:最大与最小——MAX_VALUE、MIN_VALUE
6:数据类型转换
A:自动类型转换,条件
a:转换前的数据类型与转换后的类型兼容
b:转换后的数据类型的表示范围要比转换前的类型大
B:强制类型转换
(float)a/b=a/(float)b=(float)a/(float)b
7:运算符
A:一元运算符
~a——取a的补码
8:表达式的类型转换——“以不流失数据位前提”
四——数组与方法
1:int array[];//声明一维数组
array = new int[个数];//分配内存给数组
2:数组常用方法——arraycopy()、sort()
3:二维数组——int score[][];
score = new int[4][3];//配置一块内存空间,供4行3列的整型数组score使用
java允许二维数组中每行的元素个数均不相同
4:方法的重载
方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或类型不同即可
第二部分——Java面向对象程序设计
五——类的基本形式
1:类的多态
Java语言中含有方法重载与成员覆盖两种形式的多态
方法重载——在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同
成员覆盖——子类和父类允许具有相同的变量名称,但数据类型不同,允许具有相同的方法名称,但完成的功能不同
2:类与对象
由图中可以看出,当语句执行到Person p 的时候,只是在栈内存中声明了一个Person的对象p,但是这个时候p并没有在堆内存中开辟空间,所以这个时候的p是一个未实例化的对象,用new关键字实际上就是开辟堆内存,把堆内存的引用赋给了p,这个时候的p才称为一实例化对象
3:匿名对象——就是没有明确声明的对象。也可以理解为只使用一次的对象,即没有任何一个具体的对象名称引用它
ru:System.out.println(new Person().talk());
4:对象的比较——“==”运算符用于比较两个对象的内存地址值是否相等,equals()方法用于比较两个对象的内容是否一致
5:构造方法
A:如果在程序中想用某一构造方法调用另一构造方法,可以用this来实现——this();
6:static关键字——static声明的变量为静态变量(类变量)
在图中可以发现,所有的对象都指向同一个city属性,只要当中有一个对象修改了city属性的内容,则所有对象都会被同时修改
7:静态方法
8:静态代码块——一个类可以使用不包含任何方法体重的静态代码块,当类被载入时,静态代码块被执行,且只执行一次。静态代码块经常用来进行类属性的初始化
9:内部类——在外部引用内部类;在方法中定义内部类
六——类的继承
1:子类构造方法的第一行默认隐含了一个“super()”语句
2:复写——子类复写父类中的方法时,被子类复写的方法不能拥有比父类中更严格的访问权限
3:抽象类
A:抽象类和抽象方法都必须用abstract关键字来修饰
B:抽象类不能被实例化,也就是不能用new关键字去产生对象
C:抽象方法只需声明,而不需实现
D:含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类
4:final关键字——声明类、属性和方法时,可以使用
A:final标记的类不能被继承
B:final标记的方法不能被子类复写
C:final标记的变量(成员变量或局部变量)即为常量,只能赋值一次
5:接口
A:接口里的数据成员必须初始化,且数据成员均为常量
B:接口里的方法必须全部声明为abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是”抽象方法“
6:对象的多态性
A:向上转型——父类对象通过子类对象去实例化,实际就是对象的向上转型。向上转型不需要进行强制类型转换,但是向上转型会丢失精度
B:向下转型——也就是说父类的对象可以转换成子类的对象,需要进行强制的类型转换
7:instanceof关键字——可以判断一个类是否实现了某个接口,也可以用它来判断一个实例对象是否属于一个类
对象 instanceof 类(或接口)
8:接口对象的实例化——接口是无法直接实例化的,因为接口中没有构造方法,但是却可以根据对象的多态性,通过接口的子类对其进行实例化
9:匿名内部类(anonymous inner class)——好处是可以利用内部类创建不具有名称的对象,并利用它访问到类里的成员
七——异常处理
1:常见的异常
A:算术异常(ArithmeticException)
B:没有给对象开辟内存空间时会出现空指针异常(NullPointerException)
C:找不到文件异常(FileNotFoundException)
2:在程序中抛出异常、指定方法抛出异常、编写自己的异常类
八——包及访问权限
1:包(package)的基本概念
2:import语句
3:类成员的访问控制权限
A:private——成员只能在这个类的内部使用
B:default——默认访问控制符,可以被这个包中的其他类访问。如果子类与父类位于不同的包中,子类也不能访问父类中的default成员
C:protected——既可以被同一个包中的其他类访问,也可以被不同包中的子类访问
D:public——任何
4:java命名习惯
A:包名中的字母一律小写
B:类名、接口名应当使用名词,每个单词的首字母大写
C:方法名,第一个单词小写,后面每个单词首字母大写
D:常量的每一个字母都大写
5:jar文件的使用——Java Archive File,是一种压缩文件,通常称为jar包
只要在命令行中用以下的命令就可以讲一个包打成一个jar文件——jar -cvf create.jar demo
-c:创建新的存档
-v:生成详细输出到标准输出上
-f:指定存档文件名
create.jar:是生成jar文件的名称
demo:要打成jar文件的包
第三部分——Java程序应用
九——多线程
1:进程与线程
2:通过继承Thread类实现多线程
public class ThreadDemo { public static void main(String args[]) { new TestThread().start();//调用Thread类中的start()方法,实际是调用run()方法 //输出 for(int i=0; i<10; i++) { System.out.println("main 线程在运行"); } } } //TestThread类继承了Thread类,此类实现了多线程 class TestThread extends Thread { public void run() { for(int i=0; i<10; i++) { System.out.println("TestThread在运行"); } } }
3:通过Runnable接口实现多线程
public class ThreadDemo { public static void main(String args[]) { //产生Runnable接口的子类实例化对象 TestThread t = new TestThread(); //通过Thread类的start()方法,启动多线程 new Thread(t).start(); //输出 for(int i=0; i<10; i++) { System.out.printlf("main线程在运行"); } } } //TestThread通过实现Runnable接口,实现多线程 class TestThread implements Runnable { public void run() { //输出 for(int i=0; i<10; i++) { System.out.printlf("TestThread线程在运行"); } } }
4:两种方式的比较——Thread类实现了Runnable接口,也就是说Thread类也是Runnable接口的一个子类
区别——一个类继承Thread类之后,这个类的对象无论调用多少次start()方法,结果都只有一个线程在运行
5:线程的状态——每个java程序都有一个缺省的主线程。实际上运行java时,就启动了一个JVM的进程,默认会产生两个线程:main()方法的线程,另外一个就是垃圾回收(GC)线程
6:后台线程与setDaemon()方法——只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程在运行,这个进程就会结束。
如果某个线程对象在启动(调用start()方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程
7:线程的强制运行——join()
线程的休眠——sleep()
线程的中断——interrupt()
8:多线程的同步问题
A:同步代码块——synchronized(this)
synchronized(对象)
{
需要同步的代码;
}
B:函数实现同步——只要在需要同步的函数定义前加上synchronized关键字即可
访问控制符 synchronized 返回值类型 方法名称(参数){......}
9:死锁——一旦有多个进程,且他们都要争用对多个锁的独占访问,那么就可能发生死锁。如果有一组进程或线程,其中每一个都在等待一个只有其他进程或线程才可以执行的操作,那么就称他们被死锁了
最常见的死锁形式是当线程1持有对象A上的锁,而且正在等待对象B上的锁;而线程2持有对象B上的锁,却正在等待对象A上的锁。这两个线程永远都不会获得第二个所,或是释放第一个锁,所以他们会永远等待下去
要避免死锁,应该确保在获取多个锁时,在所有的线程中都以相同的顺序获取锁
10:线程间通信——Java是通过Object类的wait、notify、notifyAll这几个方法来实现线程间的通信的。
A:wait——告诉当前线程放弃监视器并进入休眠状态,直到其他线程进入同一监视器并调用notify为止
B:notify——唤醒同一对象监视器中调用wait的第一个线程。
C:notifyAll——唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行
上面这三个方法只能在synchronzied方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁标记
class Person { private String name="李四"; private String sex="女"; boolean bFull = false; public synchronized void set(String name, String sex) { if(bFull) { try { wait();//后来的线程要等待 } catch(InterruptedException e) {} } this.name = name; try { Thread.sleep(10); } catch(Exception e) { System.out.println(e.getMessage()); } this.sex=sex; bFull=true; notify();//唤醒最先达到的线程 } public synchronized void get() { if(!bFull) { try { wait(); } catch(InterruptedException e) {} } System.out.println(name + "---->" + sex); bFull = false; notify(); } }
10:线程生命周期的控制
控制线程生命周期的方法有很多,如:suspend方法、resume方法和stop方法。但这三个方法都不推荐使用,其中不推荐使用suspend和resume的原因是:
A:会导致死锁的方生
B:它允许一个线程(甲)通过直接控制另外一个线程(乙)的代码来直接控制那个线程(乙)
虽然stop能够避免死锁的发生,但也有其不足——如果一个线程正在操作共享数据段,操作刚才没有完成就被“stop”了的话,将会导致数据的不完整性
package javatest; public class ThreadLife { public static void main(String[] args) { TestThread t = new TestThread(); new Thread(t).start(); for(int i=0; i<10; i++) { if(i==5) t.stopMe(); System.out.println("Main 线程正在运行"); } } } class TestThread implements Runnable { private boolean bFlag = true; public void stopMe() { bFlag=false; } @Override public void run() { while(bFlag) { System.out.println(Thread.currentThread().getName() + " 在运行"); } } }
通过控制run方法中循环条件的方式来结束一个线程的方法是推荐使用的,也是实际用得最多的方法
十——文件(IO)操作
1:File类——代表磁盘文件本身的对象
2:RandomAccessFile类——提供了众多的文件访问方法。支持“随机访问”方式
new RandomAccessFile(f, "rw")//读写方式
new RandomAccessFile(f, "r")//只读方式
注意:当程序需要以读写方式打开一个文件时,如果这个文件不存在,程序就会自动创建此文件
3:流类——java的流式输入、输出建立在四个抽象类的基础上:InputStream,OutputStream,Reader和Writer
InputStream和OutputStream设计成字节流类。Reader和Writer为字符类设计
一般来说,处理字符或字符串时应该使用字符流类,处理字节或二进制对象时应该用字节流类
不管是操作字节流还是字符流都按照下面的方式进行
A:使用File类找到一个文件
B:通过File类的对象去实例化字节流或字符流的子类
C:进行字节(字符)的读、写操作
D:关闭文件流
3.1:字节流——字节流以InputStream和OutputStream为顶层
3.1.1:InputStream(输入字节流)。该类所有方法在出错条件下都会引发一个IOException异常
3.1.2:OutputStream(输出字节流)
注:上面两个表中的多数方法由InputStream和OutputStream的子类实现,mark()和reset()除外
3.1.3:FileInputStream(文件输入流)——创建一个能从文件读取字节的InputStream类,常用构造方法:
FileInputStream(String filepath)//文件绝对路径
FileInputStream(File fileObj)//fileObj是描述该文件的File对象
InputStream f0 = new FileInputStream("c:\\test.txt"); File f = new File("c:\\test.txt"); InputStream f1 = new FileInputStream(f)
第一个构造方法更常用,而第二个方法则允许在把文件赋给输入流之前用File方法更进一步检查文件
3.1.4:FileOutputStream(文件输出流)——创建一个可以向文件写入字节的类,常用构造方法:
FileOutputStream(String filePath)
FileOutputStream(File fileObj)
FileOutputStream(String filePath, boolean append)//若append为true,文件则以设置搜索路径模式打开
FileOutputStream的创建不依赖于文件是否存在。创建对象时,FileOutputStream会在打开输出文件之前就创建它。这种情况下如果试图打开一个只读文件会引发IOException
3.2:字符流——字节流可以处理任何类型的输入、输出操作,但是它们不能直接Unicode字符。字符流的层次结构的顶层是Reader和Writer抽象类
3.2.1:Reader——定义java的流式输入模式的抽象类
3.2.2:Writer——定义流式字符输出的抽象类
3.2.3:FileReader类创建一个可以读取文件内容的Reader类
FileReader(String filePath) FileReader(File fileObj)
3.2.4:FileWiter类创建一个可以写文件的Writer类。不依赖于文件创建与否。创建文件之前,它将在创建对象时打开它来作为输出
FileWiter(String filePath)
FileWiter(String filePath, boolean append)//如果append为ture,输出是附加到文件尾的
FileWiter(File fileObj)
注:FileWriter类并不是直接继承自Writer类,而是继承了Writer的子类——OutputStreamWriter,此类为字节流和字符流的转换类。也就是说真正从文件中读取进来的数据还是字节,只是在内存中将字节转换成了字符。所以,字符流用到了缓冲区,而字节流没有用到缓冲区。另外可以用Writer类中的flush()方法,强制清空缓冲区
3.3:管道流——主要作用是可以连接两个线程间的通信。
管道流也分为字节流(PipedInputStream, PipedOutputStream)与字符流(PipedReader, PipedWriter)
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道。PipedOutputStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据
package javatest; import java.io.*; public class PipeStreamDemo { public static void main(String args[]) { try { Sender sender = new Sender();//产生两个线程对象 Receiver receiver = new Receiver(); PipedOutputStream out = sender.getOutputStream();//写入 PipedInputStream in = receiver.getInputStream();//独处 out.connect(in);//将输出发送到输入 sender.start();//启动线程 receiver.start(); } catch(IOException e) { System.out.println(e.getMessage()); } } } class Sender extends Thread { private PipedOutputStream out = new PipedOutputStream(); public PipedOutputStream getOutputStream() { return out; } public void run() { String s = new String("Receiver, 你好!"); try { out.write(s.getBytes()); out.close(); } catch(IOException e) { System.out.println(e.getMessage()); } } } class Receiver extends Thread { private PipedInputStream in = new PipedInputStream(); public PipedInputStream getInputStream() { return in; } public void run() { String s = null; byte[] buf = new byte[1024]; try { int len = in.read(buf); s = new String(buf, 0, len); System.out.println("收到了以下信息:" + s); } catch(IOException e) { System.out.println(e.getMessage()); } {} } }
3.4:ByteArrayInputStream与ByteArrayoutputStream——实现类似内存虚拟文件的功能(如果程序运行时要产生一些临时文件)
package javatest; import java.io.*; public class ByteArrayDemo { public static void main(String[] args) throws Exception { String tmp = "abcdefghijklmnopqrstuvwxyz"; byte[] src = tmp.getBytes();//src为转换前的内存块 ByteArrayInputStream input = new ByteArrayInputStream(src); ByteArrayOutputStream output = new ByteArrayOutputStream(); new ByteArrayDemo().transform(input, output); byte[] result = output.toByteArray();//result为转换后的内存快 System.out.println(new String(result)); } public void transform(InputStream in, OutputStream out) { int c = 0; try { while((c = in.read()) != -1) //read在读到流的结尾处返回-1 { int C = (int)Character.toUpperCase((char)c); out.write(C); } } catch(Exception e) { e.printStackTrace(); } } }
3.5:System.in 和 System.out
为了支持标准输入输出设备,Java定义了这两个特殊的流对象。System.in对应键盘,System.out对应显示器
3.6:打印流——PrintStream
PrintWriter
3.7:DataInputStream和DataOutputStream——提供了与平台无关的数据操作。通常会先通过DataOutputStream按照一定的格式输出,再通过DataInputStream按照一定格式读入
package javatest; import java.io.*; public class DataInputStreamDemo { public static void main(String[] args) throws IOException { //将数据写入某一种载体 DataOutputStream out = new DataOutputStream(new FileOutputStream("order.txt")); //价格 double[] prices = {18.99,9.22,14.22,5.22,4.21}; //数目 int[] units = {10,10,20,39,40}; //产品名称 String[] descs = {"T恤","被子","洋娃娃", "大头针", "钥匙链"}; //向数据过滤流写入主要类型 for(int i=0; i<prices.length; i++) { //写入价格,使用tab隔开数据 out.writeDouble(prices[i]); out.writeChar('\t'); //写入数目 out.writeInt(units[i]); out.writeChar('\t'); //写入产品名称,行尾加入换行符 out.writeChars(descs[i]); out.writeChar('\n'); } out.close(); //将数据读出 DataInputStream in = new DataInputStream(new FileInputStream("order.txt")); double price; int unit; StringBuffer desc; double total = 0.0; try { //当文本被全部读出以后会抛出EOFException,中断循环 while(true) { //读出价格 price = in.readDouble(); //跳过tab in.readChar(); //读出条目 unit = in.readInt(); in.readChar(); char chr; //读出产品名称 desc = new StringBuffer(); while((chr = in.readChar()) != '\n') { desc.append(chr); } System.out.println("定单信息: " + "产品名称:"+desc+",\t数量:" +unit+",\t价格:"+price); total = total + unit*price; } } catch(EOFException e) { System.out.println("\n总共需要:" + total+"元"); } in.close(); } }
3.8:合并流——采用SequenceInputStream类,可以实现两个文件的合并操作
package javatest; import java.io.*; public class SequenceDemo { public static void main(String[] args) throws IOException { // 声明两个文件读入流 FileInputStream in1 = null, in2 = null; // 声明一个序列流 SequenceInputStream s = null; FileOutputStream out = null; try { // 构造两个被读入的文件 File inputFile1 = new File("c:\\1.txt"); File inputFile2 = new File("c:\\2.txt"); // 构造一个输出文件 File outputFile = new File("c:\\12.txt"); in1 = new FileInputStream(inputFile1); in2 = new FileInputStream(inputFile2); // 将两输入流合为一个输入流 s = new SequenceInputStream(in1, in2); out = new FileOutputStream(outputFile); int c; while ((c = s.read()) != -1) out.write(c); in1.close(); in2.close(); s.close(); out.close(); System.out.println("ok..."); } catch (IOException e) { e.printStackTrace(); } finally { if (in1 != null) try { in1.close(); } catch (IOException e) { } if (in2 != null) try { in2.close(); } catch (IOException e) {} if (s != null) try { s.close(); } catch (IOException e) {} if (out != null) try { out.close(); } catch (IOException e) {} } } }
3.9:字符流和字节流的转换——InputStreamReader可以将一个字节流中的字节解码成字符
OutputStreamWriter将写入的字符编码成字节后写入一个字节流
为了达到最高效率,避免频繁进行字符、字节之间的转换,尽量使用BufferedWriter 类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader类。如:
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String strLine = in.readLine();//读取键盘上输入的一整行字符
package javatest; import java.io.*; public class BufferDemo { public static void main(String args[]) { BufferedReader buf = null; buf = new BufferedReader(new InputStreamReader(System.in)); String str = null; while (true) { System.out.print("请输入数字:"); try { str = buf.readLine(); } catch (IOException e) { e.printStackTrace(); } int i = -1; try { i = Integer.parseInt(str); i++; System.out.println("输入的数字修改后为:" + i); break; } catch (Exception e) { System.out.println("输入的内容不正确,请重新输入!"); } } } }
3.10:IO包中的类层次关系图
4:编码
5:对象序列化——也叫串行化,是指将对象转换成二进制数据流的一种实现手段。通过将对象序列化,可以方便的实现对象的传输及保存
Java用于序列化的类——ObjectInputStream和ObjectOutpStream。必须实现Serializable接口。但Serializable接口中没有定义任何方法,仅仅被用作一种标记,以被编译器做特殊处理——如果不希望类中的属性被序列化,可以在声明属性之前加上transient关键字
package javatest; import java.io.*; public class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name=name; this.age=age; } public String toString() { return " 姓名: "+ this.name + " 年龄 :"+ this.age; } }
package javatest; import java.io.*; public class SerializableDemo { public static void main(String args[]) throws Exception { File f = new File("SerializedPerson"); serialize(f); deserialize(f); } //序列化对象方法 public static void serialize(File f) throws Exception { OutputStream outputfile = new FileOutputStream(f); ObjectOutputStream cout = new ObjectOutputStream(outputfile); cout.writeObject(new Person("张三", 26)); cout.close(); } //反序列方法 public static void deserialize(File f) throws Exception { InputStream inputFile = new FileInputStream(f); ObjectInputStream cin = new ObjectInputStream(inputFile); Person p = (Person)cin.readObject(); System.out.println(p); } }
十一——Java Applet程序
十二——Java常用类库
1:API概念——Application Programming Interface 应用程序编程接口
2:String和StringBuffer
String x = "a" + 4 + "c";
编译时等效
String x = new StringBuffer().append("a").append(4).append("c").toString();
开发中,如果需要频繁改变字符串的内容就需要考虑用StringBuffer。
3:基本数据类型的包装类——Java对数据既提供基本数据的简单类型,也提供了相应的包装类(也叫包装类)。
4:System类和Runtime类
4.1:Java不支持全局函数和变量,Java设计者将一些系统相关的重要函数和变量收集到了一个统一的类中,这就是System类。此类中的所有成员都是静态的。
exit(int status)方法,提前终止虚拟机的运行。对于发生了异常情况而想终止虚拟机的运行,传递一个非零值作为参数。若在用户正常操作下,终止虚拟机的运行,传递零值作为蚕食
CurrentTimeMills方法返回自1970年1月1日0点0分0秒起至今的以毫秒为单位的时间,这是一个龙类型的大数值。在计算机内部,只有数值,没有真正的日期类型及其他各种类型,也就是说,平常用到的日期本质就是一个数值,但通过这个数值,能够推算出其对应的具体日期时间
getProperties方法获取当前虚拟机的环境属性
4.2:Runtime类——封装了Java命令本身的运行进程。不能创建实例,但可以通过Runtime.getRuntime方法获取运行的Runtime对象的引用
Java命令运行后,本身是多任务操作系统上的一个进程,在这个进程中启动一个新的进程,即执行其他程序时使用exec方法。exec方法返回一个代表子进程的Process类对象,通过这个对象,Java进程可以与子进程交互
public class RuntimeDemo { public static void main(String[] args) { Runtime run = Runtime.getRuntime() ; try { run.exec("notepad.exe") ; } catch (Exception e) { e.printStackTrace(); } } }
运行程序之后,可以发现程序已经为读者打开了记事本程序。所以通过Runntime类可以为开发者执行操作系统的可执行程序。
5:Date与Calendar、DataFormat类
Date类用于表示日期和时间,最简单的构造函数是Date(),它以当前的日期和时间初始化一个Date对象。由于开始设计Date时没有考虑到国际化,所以后来又设计了两个新的类来解决Date类中的问题,一个是Calendar类,一个是DateFormat类。
Calendar类是一个抽象基类,主要完成日期字段之间相互操作的功能,如Calendar.add方法可以实现在某一日期的基础上增加若干天(或年、月、小时、分、秒等日期字段)后的新日期,Calendar.get方法可以取出日期对象中的年、月、日、小时、分、秒等日期字段的值,Calendar.set方法修改日期对象中的年、月、日、小时、分、秒等日期字段的值。Calendar.getInstance方法可以返回一个Calendar类型(更确切地说是它的某个子类)的对象实例,GregorianCalendar类是JDK目前提供的一个惟一的Calendar子类,Calendar.getInstance方法返回的就是预设了当前时间的GregorianCalendar类对象。
6:Math与Random类
7:类集框架
Java的类集——Collection
7.1类集接口
除了类集接口之外,类集也使用Comparator,Iterator和ListIterator接口。
由Collection 定义的方法
7.1.2:List接口——List接口扩展了Collection并声明存储一系列元素的类集的特性。使用一个基于零的下标,元素可以通过它们在列表中的位置被插入和访问。一个列表可以包含复制元素
7.1.3:集合接口——集合接口定义了一个集合。它扩展了Collection并说明了不允许复制元素的类集的特性。因此,如果试图将复制元素加到集合中时,add()方法将返回false。它本身并没有定义任何附加的方法。
7.1.4:SortedSet接口——SortedSet接口扩展了Set并说明了按升序排列的集合的特性。除了那些由Set定义的方法之外,由SortedSet接口说明的方法列在表12-4中。
7.2:Collection接口
7.2.1:ArrayList类——支持可随需要而增长的动态数组。标准数组是定长的。注意:动态数组也被从以前版本遗留下来的类Vector所支持。
7.2.2:LinkedList类——提供了一个链接列表的数据结构
7.2.3:HashSet类——
HashSet扩展AbstractSet并且实现Set接口。它创建一个类集,该类集使用散列表进行存储。而散列表通过使用称之为散列法的机制来存储信息。
在散列(hashing)中,一个关键字的信息内容被用来确定唯一的一个值,称为散列码(hash code)。而散列码被用来当做与关键字相连的数据的存储下标。关键字到其散列码的转换是自动执行的——看不到散列码本身。程序代码也不能直接索引散列表。散列法的优点在于即使对于大的集合,它允许一些基本操作,如:add(),contains(),remove()和size( )方法的运行时间保持不变。
7.2.4:TreeSet类——TreeSet为使用树来进行存储的Set接口提供了一个工具,对象按升序存储。访问和检索是很快的。在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个很好的选择。
7.3:通过迭代的方法访问类集——使用iterator,iterator是一个或者实现Iterator或者实现ListIterator接口的对象。Iterator可以完成通过循环输出类集内容,从而获得或删除元素。ListIterator扩展Iterator,允许双向遍历列表,并可以修改单元。
7.3.1:使用迭代方法
在通过迭代方法访问类集之前,必须得到一个迭代方法。每一个Collection类都提供一个iterator( )方法,该方法返回一个对类集的迭代方法。通过使用这个迭代方法对象,可以一次一个地访问类集中的每一个元素。通常,使用迭代方法通过循环输出类集的内容,步骤如下:
1、通过调用类集的iterator( )方法获得对类集的迭代方法。
2、建立一个调用hasNext( )方法的循环,只要hasNext( )返回true,就进行循环迭代。
3、在循环内部,通过调用next( )方法来得到每一个元素。
7.4:处理映射(map)——映射(map)是一个存储关键字和值的关联或者说是”关键字/值“对的对象
7.4.1:映射接口
7.5:从以前版本遗留下来的类和接口
由java.util定义的从以前版本遗留下来的类说明如下:
Dictionary Hashtable Properties Stack Vector
有一个枚举(Enumeration)接口是从以前版本遗留下来(已经被Iterator所替代)。
7.5.1:Vector——实现动态数组,与ArrayList相似,但是Vector是同步的
7.5.2:Stack——是Vector的一个子类,实现标准的后进先出堆栈
7.5.3:Dictionary——字典(Dictionary)是一个表示“关键字/值”存储库的抽象类,同时它的操作也很像映射(Map)。已经被映射(map)取代
7.5.4:Hashtable——散列表(Hashtable)是原始java.util中的一部分同时也是Dictionary的一个具体实现。然而,Java 2重新设计了散列表(Hashtable)以便它也能实现映射(Map)接口。因此现在Hashtable也被集成到类集框架中。它与HashMap相似,但它是同步的。和HashMap一样,Hashtable将“关键字/值”对存储到散列表中。使用Hashtable时,指定一个对象作为关键字,同时指定与该关键字相关联的值。接着该关键字被散列,而把得到的散列值作为存储在表中的值的下标。
9:对象克隆——所有具有clone功能的类都有一个特性,那就是它直接或间接地实现了Cloneable接口
十三——Java网络程序设计
1:Socket——Java提供了两种类型的网络程序实现:面向连接(TCP)、面向无连接(UDP)。
package javatest; import java.io.*; import java.net.*; public class HelloServer { public static void main(String[] args) throws IOException { ServerSocket serversocket=null; PrintWriter out=null; try { //实例化了一个服务端的Socket连接 serversocket=new ServerSocket(9999); } catch(IOException e) { System.err.println("Could not listen on port:9999."); System.exit(1); } Socket clientsocket = null; try { //accept()方法用来监听客户端的连接 clientsocket = serversocket.accept(); } catch(IOException e) { System.err.println("Accept failed."); System.exit(1); } out = new PrintWriter(clientsocket.getOutputStream(), true); out.println("hello world!"); clientsocket.close(); serversocket.close(); } }
package javatest; import java.io.*; import java.net.*; public class HelloClient { public static void main(String[] arg) throws IOException { Socket hellosocket=null; BufferedReader in=null; //下面这段程序,用来将输入输出流与socket关连 try { hellosocket=new Socket("localhost", 9999); in=new BufferedReader(new InputStreamReader(hellosocket.getInputStream())); } catch(UnknownHostException e) { System.err.println("Don't know about host:localhost!"); System.exit(1); } catch(IOException e) { System.err.println("Couldn't get I/O for the connection."); System.exit(1); } System.out.println(in.readLine()); in.close(); hellosocket.close(); } }
2:Socket的经典范例—— Echo程序:
package javatest; import java.io.*; import java.net.*; public class EchoServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; PrintWriter out = null; BufferedReader in = null; try { //实例化监听端口 serverSocket = new ServerSocket(1111); } catch(IOException e) { System.err.println("Could not listen on port:1111."); System.exit(1); } Socket incoming = null; while(true) { incoming = serverSocket.accept(); out = new PrintWriter(incoming.getOutputStream(), true); //先将字节流通过InputStreamReader转换为字符流,然后将字符流放入缓冲中 in = new BufferedReader(new InputStreamReader(incoming.getInputStream())); //提示信息 out.println("Hell!..."); out.println("Error BYE to exit"); out.flush(); //没有异常的情况不断循环 while(true) { //只有当用户输入的时候才返回数据 String str = in.readLine(); //当用户连接断掉时会返回空值 if(str==null) { //退出循环 break; } else { //对用户输入字串加前缀Echo:,将此信息打印到客户端 out.println("Echo:" + str); out.flush(); //退出命令,equalsIgnoreCase()是不区分大小写的比较 if(str.trim().equalsIgnoreCase("BYE")) break; } } //收尾工作 out.close(); in.close(); incoming.close(); serverSocket.close(); } } }
package javatest; import java.io.*; import java.net.*; public class EchoClient { public static void main(String[] args) throws IOException { Socket echoSocket = null; PrintWriter out = null; BufferedReader in = null; try { echoSocket = new Socket("localhost", 1111); out=new PrintWriter(echoSocket.getOutputStream(),true); in=new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); } catch(UnknownHostException e) { System.err.println("Don't know about host: localhost."); System.exit(1); } System.out.println(in.readLine()); System.out.println(in.readLine()); BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); String userInput; // 将客户端Socket输入流(既服务器端Socket的输出流)输出到标准输出上 while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println(in.readLine()); } out.close(); in.close(); echoSocket.close(); } }
3:多线程Socket
package socketthread; import java.io.*; import java.net.*; public class EchoMultiServerThread extends Thread { private Socket socket=null; public EchoMultiServerThread(Socket socket) { super("EchoMultiServerThread"); //声明一个socket对象 this.socket = socket; } public void run() { try { PrintWriter out = null; BufferedReader in = null; out=new PrintWriter(socket.getOutputStream()); in=new BufferedReader(new InputStreamReader(socket.getInputStream())); out.println("Hello!..."); out.println("Enter BYE to exit"); out.flush(); while(true) { String str=in.readLine(); if(str==null) { break; } else { out.println("Echo:" + str); out.flush(); if(str.trim().equalsIgnoreCase("BYE")) break; } } out.close(); in.close(); socket.close(); } catch(IOException e) { e.printStackTrace(); } } }
package socketthread; import java.io.*; import java.net.*; public class EchoServerThread { public static void main(String[] args) throws IOException { //声明一个ServerSocket ServerSocket serverSocket=null; //声明一个监听标识 boolean listening=true; try { serverSocket = new ServerSocket(1111); } catch(IOException e) { System.err.println("Could not listen on port:1111."); System.exit(1); } //如果处于监听态则开启一个线程 while(listening) { //实例化一个服务端的socket与请求socket建立连接 new EchoMultiServerThread(serverSocket.accept()).start(); } //将serverSocket的关闭操作房子循环外,只有当监听为false时,服务才关闭 serverSocket.close(); } }
4:DatagramSocket程序——因为建立流套接字的每个连接都要花费一定时间,要减少这种开销,网络API提供了第二种套接字:自寻址套接字(datagram socket),使用UDP发送寻址信息(从客户程序到服务程序或从服务程序到客户程序)。不同的是可以通过自寻址套接字发送多IP信息包,自寻址信息包含在自寻址包中,此外自寻址包又包含在IP包内,这就将寻址信息长度限制在60000字节内
与TCP保证信息到达信息目的地的方式不同,UDP提供了另外一种方法,如果自寻址信息包没有到达目的地,,那么UDP也不会请求发送者重新发送自寻址包,这是因为UDP在每一个自寻址包中包含了错误检测信息,在每个自寻址包到达目的地之后UDP只进行简单的错误检查,如果检测失败,UDP将抛弃这个自寻址包,也不会从发送者那里重新请求替代者,这与通过邮局发送信件相似,发信人在发信之前不需要与收信人建立连接,同样也不能保证信件能到达收信人那里
自寻址套接字工作常用的类包括下面两个类:DatagramPacket和DatagramSocket。DatagramPacket对象描绘了自寻址包的地址信息,DatagramSocket表示客户程序和服务程序自寻址套接字,这两个类均位于java.net包内。
DatagramPacket类
在使用自寻址包之前,需要首先熟悉DatagramPacket类,地址信息和自寻址包以字节数组的方式同时压缩入这个类创建的对象中。
DatagramPacket有数个构造方法,即使这些构造方法的形式不同,但通常情况下他们都有两个共同的参数:byte [] buffer 和 int length,buffer参数包含了一个对保存自寻址数据包信息的字节数组的引用,length表示字节数组的长度。
最简单的构造方法是DatagramPacket(byte [] buffer, int length),这个构造方法确定了自寻址数据包数组和数组的长度,但没有任何自寻址数据包的地址和端口信息,这些信息可以通过调用方法setAddress(InetAddress addr)和setPort(int port)添加上。
DatagramSocket类
DatagramSocket类在客户端创建自寻址套接字与服务器端进行通信连接,并发送和接受自寻址套接字。虽然有多个构造方法可供选择,但发现创建客户端自寻址套接字最便利的选择是DatagramSocket()方法,而服务器端则是DatagramSocket(int port)方法,如果未能创建自寻址套接字或绑定自寻址套接字到本地端口,那么这两个方法都将抛出一个SocketException对象,一旦程序创建了DatagramSocket对象,那么程序分别调用send(DatagramPacket dgp)和 receive(DatagramPacket dgp)来发送和接收自寻址数据包
package socketthread; import java.io.*; import java.net.*; public class UdpRecevier { public static void main(String[] args) { DatagramSocket ds=null; byte[] buf=new byte[1024]; DatagramPacket dp=null; try { ds=new DatagramSocket(9000); } catch(SocketException e) {} //创建DatagramPacket时,要求的数据格式是byte型数组 dp=new DatagramPacket(buf, 1024); try { ds.receive(dp); } catch(IOException ex1){} //调用public String(byte[] bytes, int offset, int length)构造方法 //将byte型的数组转换成字符串 String str = new String(dp.getData(), 0, dp.getLength()) + "from " + dp.getAddress().getHostAddress() + ":" + dp.getPort(); System.out.println(str); ds.close(); } }
package socketthread; import java.io.*; import java.net.*; public class UdpSender { public static void main(String[] args) { //要编写出UDP网络程序,首先要用到java.net.DatagramSocket类 DatagramSocket ds = null; DatagramPacket dp = null; try { ds=new DatagramSocket(3000); } catch(SocketException ex) {} String str = "hello world"; try { //调用InetAddress.getByName()方法可以返回一个InetAddress类的实例对象 dp=new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("localhost"), 9000); } catch(UnknownHostException ex1){} try { ds.send(dp); }catch(IOException ex2){} ds.close(); } }