• 1. 系统学习Java面向对象语言 成为一名熟练的Java程序员 帮助获得SCJP证书 参考书籍:《Java面向对象编程》 技术支持网址:www.javathinker.org 讲解:孙卫琴 linda_j2ee@yahoo.com.cn Java 编程培训讲义
  • 2. 第1课 起 步 描述Java编程语言的主要特性 描述Java虚拟机的主要功能 描述垃圾收集是如何进行的 列举Java虚拟机在运行时所执行的三大任务 定义Class,Package和Application 编写、编译并运行简单Java应用程序 了解JavaDoc文档的作用 了解java. lang包 参照《Java面向对象编程》的第二章(第一个Java应用)
  • 3. Java编程语言的主要特性面向对象 可移植性, 跨平台 支持分布式的网络应用 安全性和健壮性Java类(.class文件)Java虚拟机Java虚拟机 WindowsUnixJava客户程序Java服务器程序网络万物皆对象
  • 4. Java虚拟机概念 Java虚拟机(Java Virtual Machine)在实际的计算机上通过软件模拟来实现。 Java虚拟机有自己想象中的硬件。 功能 提供垃圾回收功能 提供运行时环境
  • 5. 提供运行时环境执行三大任务 加载代码 校验代码 执行代码
  • 6. 一个Java应用程序 /** * HelloWorldApp.java */ public class HelloWorldApp{ public static void main (String args[]) { System.out.println ("Hello World!"); } }
  • 7. 类和包介绍类(class)是描述提供某种功能的模块,类是对象的模版。 类被划分到不同的包(package)中,每个包都包含几个类。 JDK的Java类库中的几个重要包: java.lang, java.io, java.awt , java.net , java.util学生小张学生小王孙老师陈老师客户小红客户小芳售货员小丁售货员小东Student类Teacher类Customer类Seller类myapp.school包myapp.store包对象
  • 8. Java源文件布局包含三个“顶级”要素 package声明语句 import引入语句 类声明语句 每一个源文件中最多只能有一个公共访问类(用public修饰的类) ---B.java----------- package b; // package声明语句 import a .*; // import引入语句,或者import a.A; public class B{ //类声明语句 public static void main(String args[]){ A a; //access class A a=new A(); } }
  • 9. 源文件布局例题:Which of the following will compile without error ? a)import java.awt.*; package mypackage; class Myclass {} b)package myPackage; import java.awt.*; class MyClass{} c) /*This is a comment */ package myPackage; import java.awt.*; public class MyClass{}
  • 10. 程序入口main()方法的声明以下两种方式是合法的main()方法声明: 1. public static void main(String args[]) 2.static public void main(String args[])public class HelloWorld{ public static int main(String args[]){ System.out.println("hello world"); return 0; } }
  • 11. 程序入口main()方法的声明例题:Which declarations for the main() method in a stand-alone program are NOT valid? a) public static void main() b) public static void main(String[] string) c) public static void main(String args) d) static public int main(String[] args) e) static void main(String[] args)
  • 12. JDK简介下载JDK: http://java.sun.com/javase/downloads/index.jsp JDK 是Java Development Kit(Java开发工具包)的缩写。它为Java应用程序提供了基本的开发和运行环境。目前JDK的最成熟的版本为JDK1.5,也称为JDK5.0,它还有一个吸引人的商业名叫Tiger。 JDK主要包括以下内容: Java虚拟机:负责解析和执行Java程序。Java虚拟机可以运行在各种操作系统平台上。 JDK类库:提供了最基础的Java类以及各种实用类。java.lang、java.io、java.util、java.awt、javax.swing和java.sql包中的类都位于JDK类库中。 开发工具:这些开发工具都是可执行程序,主要包括:javac.exe(编译工具)、java.exe(运行工具)、javadoc.exe(生成JavaDoc文档的工具)和jar.exe(打包工具)等。
  • 13. 安装JDK在本地机器上安装JDK 假定JDK安装到本地后的根目录为,在\bin目录下提供了以下工具: javac.exe :Java编译器,把Java源文件编译成Java类文件。 jar.exe : Java应用的打包工具。 java.exe : 运行Java程序。 javadoc.exe :JavaDoc文档生成器。 为了便于在DOS命令行下直接运行这些工具,可以把/bin目录添加到操作系统的系统环境变量PATH变量中。
  • 14. 练习1:编译并运行如下代码public class HelloWorldApp{ public static void main (String args[]) { System.out.println ("Hello World!"); } } HelloWorldApp.javaHelloWorldApp.classjavac HelloWorldApp.java
  • 15. 练习2:编译并运行如下代码public class ParamTester{ public static void main(String argv[]){ System.out.println(argv[0]); System.out.println(argv[1]); } } 运行程序的命令为:java ParamTester good morning *理解命令行参数的传递
  • 16. 练习3:编译并运行如下代码编译并运行A.java,B.java B.classA.classB.classpackage apackage b/** A.java */ package a; public class A{ public A(){ System.out.println("new Instance of A"); } }/** B.java */ package b; import a .*; //import package a public class B{ public static void main(String args[]){ A a; //access class A a=new A(); } }
  • 17. 练习3:编译并运行如下代码编译并运行A.java,B.java 理解包的结构和目录的对应关系,会使用java, javac命令的参数选项 examplessrcclassesababA.javaB.javaA.classB.class
  • 18. 练习3:编译并运行如下代码通过JDK来编译和运行的命令如下: 在当前目录下输入命令: javac -sourcepath src -d classes src\b\B.java java -classpath classes b.B
  • 19. 第2课 标识符、关键字和数据类型 区分有效和无效标识符 确认Java关键字 列出八个基本类型 为数字类型和String类型定义文字值 为一个简单的包含基本类型成员变量的类创建一个类定义 使用new构造一个对象 描述默认初始化 使用点符号访问一个对象的成员变量 参照《Java面向对象编程》的第3章(数据类型和变量)
  • 20. 注释有三种插入注释的风格: //comment on one line /* comment on one or more line */ /** documenting comment */
  • 21. javadoc注释以“/**”开始,以“*/”结束。 javadoc命令只处理源文件中在类、接口定义、方法、变量、构造器之前的注释. 例:运行javadoc MyHelloworld.java /** * 在main( )方法中使用的字符串 * @see #main(java.lang.String[]) */ private static String sDisplay="HelloWorld"; /** * 显示HelloWorld * @param args 从命令行中 */ mvvv public static void main(String args[]) javadoc MyHelloworld.java
  • 22. 关键字Java语言的关键字有:abstract、boolean、break、byte、case、catch、char、class、continue、default、do、double、else、extends、false、final、finally、float、for、if、implements、import、instanceof、int、interface、long、native、new、null、package、private、protected、public、return、short、static、super、switch、synchronized、this、throw、throws、transient、true、try、void、volatile、while。 Java语言保留字有:const、goto。 区分Java关键字和其他语言的关键字(sizeof, friendly,NULL,String)
  • 23. 标识符标识符的规则($123, _param1 ): 首字母只能是a-z、A-Z、$或者_ 其余字母只能是a-z、A-Z、$、_或者数字 例题:Which of the following identifiers are ILLEGAL? a) _underscore b) 5Interstate c) Interstate5 d) _5_
  • 24. Java数据类型基本类型 逻辑类 boolean 字符类 char 整数类 byte, short, int, long 浮点类 double, float 引用类型 对象引用类型 数组引用类型
  • 25. 基本类型的取值范围boolean isMarried=true; int age=18; char grade=‘A’; double price=44.13;
  • 26. 直接数(literal value)直接数就是直接显式赋给某个变量的 具体数值。共有7种类型的直接数: int型直接数(1, 1567) long型直接数(22L,22l) float型直接数(234.5F,1.3f) double型直接数(234.5 , 235.6D,11.0d) boolean型直接数(true, false) char型直接数(‘a’, ‘\u000F’) String型直接数(“hello”) int score=99; long length=100L; float weight=14.12F; char sex=‘M’; String name=“Tom”;字符的16进制Unicode编码
  • 27. 引用类型对象引用类型 数组引用类型 Student tom; tom=new Student(); int[] intArray=new int[3]; int age=18;
  • 28. 用new关键字创建对象第一步:分配内存空间 第二步:初始化为其变量类型的默认值 第三步:调用构造方法 第四步:返回对象实例的引用 public class Sample{ byte memberV1; int memberV2; public Sample(){ memberV2=2;} //构造方法 public static void main(String args[]){Sample s=new Sample();} } 堆区Sample对象 memberV1(占1个字节,值为0) memberV2(占4个字节,值为2) 引用变量S
  • 29. 数组1. 声明 int[] intArray; 或者int intArray[]; 2. 构造 intArray=new int[3]; 3.初始化 for(int i=0;i<3;i++){ intArray[i]=i; } 等价于:int intArray[]= new int[] {0,1,3}; 等价于: int intArray[]= {0,1,3}; 堆区int数组 int[0](占4个字节,值为0) int[1](占4个字节,值为0) int[2](占4个字节,值为0) 引用变量intArrayint intArray[3]; //非法的数组声明new语句的作用左边三步等价于: int[] intArray=new int[]{0,1,2}; 或者: int[] intArray= {0,1,2};
  • 30. 多维数组String[][] arrays=new String[2][]; arrays[0]=new String[1]; arrays[1]=new String[3]; arrays[0][0]=”example1”; arrays[1][0]=”example2”; arrays[1][1]=”example3”; 堆区arrays[0]arrays[1]arrays[0][0]”example1””example2””example3”arrays[1][0]arrays[1][1]arrays[1][2]=nullarrays引用变量
  • 31. 数组的特性数组的索引从0开始, length-1结束。 公共属性变量length 例1:What will happen if you try to compile and run the following code? public class Q { public static void main(String argv[]){ int a[]=new int[]{1,2,3}; System.out.println(a[1]); System.out.println(a[a.length-1]); } }
  • 32. 变量的默认值在创建一个类的对象时,Java 会自动为它的成员变量初始化 为默认值。 数值型变量的默认值(0) 字符型的默认值(‘\u0000’) 布尔型的默认值(false) 引用类型的默认值(null)。 一个数组变量的默认值(null) 数组的所有元素初始化为其相应类型的默认值。 public class Sample{ int v1; boolean v2; Sample v3; long[] v4=new long[3]; String[] v5=new String[3]; public static void main(String args[]){ Sample sam=new Sample(); System.out.println(sam.v1); System.out.println(sam.v2); System.out.println(sam.v3); System.out.println(sam.v4[0]); System.out.println(sam.v5[0]); } }
  • 33. 第3课 表达式和流程控制区分成员变量和局部变量 描述成员变量是如何被初始化的 辨认、描述并使用Java操作符 区分合法和非法基本类型赋值 理解赋值兼容性的规律 使用if, switch,for,while和do句型结构 用break和continue控制循环流程 参照《Java面向对象编程》的第3、4和5章
  • 34. 变量的作用域成员变量:在类中声明,它的作用域是整个类。 局部变量:在一个方法的内部或方法的一个代码块的内部声明。如果在一个方法内部声明,它的作用域是整个方法;如果在一个方法的某个代码块的内部声明,它的作用域是这个代码块。代码块是指位于一对大括号{}以内的代码。 方法参数:方法或者构造方法的参数,它的作用域是整个方法或者构造方法。class Scope{ int x; //成员变量 int y; //成员变量 Scope(int x){ int y=2; int z=3; this.x=x; //this.x代表成员变量x this.y=y; //this.y代表成员变量y x=0; y=0; } public static void main(String args[]){ Scope s=new Scope(1); System.out.println(s.x); System.out.println(s.y); System.out.println(s.z); //编译出错 } }
  • 35. 变量的初始化成员变量会自动初始化 局部变量必须显式初始化再使用 class Sample{ int v1; boolean v2; public static void main(String args[]){ Sample s=new Sample(); System.out.println(s.v1); System.out.println(s.v2); } }public void doComputation() { int x = (int)(Math.random() * 100); int y; int z; if (x > 50) { y = 9; } z = y + x; // Possible use before initialization }
  • 36. 操作符操作符>>进行算术或符号右移位。 操作符>>>是不带符号右移位操作符。 操作符<<执行一个左移位 短路(short circuit)操作符与非短路操作符 字符串操作符+ 操作符 == 和对象的equals()方法(File,Date,String,包装类覆盖了equals())
  • 37. 右移位操作符>>int a1= 12 >>1; //变量a1的取值为6 int a2=-12 >> 2; //变量a2的取值为-3 int a3= 128 >> 2; //变量a3的取值为32 int a4= 129 >> 2; //变量a4的取值为32
  • 38. 逻辑右移位操作符>>>int a1= 12 >>>1; //变量a1的取值为6 int a2=-12 >>>2; //变量a2的取值为1073741821
  • 39. 左移位操作符<
  • 40. 短路(short circuit)操作符和非短路操作符“&&”和“||”是短路(short circuit)操作符,“&”和“|”是非短路操作符,它们的区别是:对于短路 操作符,如果能根据操作符左边的布尔表达式就能推算出整个表达式的布尔值,将不执行操作符 右边的布尔表达式。对于非短路操作符,始终会执行操作符两边的布尔表达式。 例题:What will happen when you attempt to compile and run the following code ? //代码段1 int output=10; boolean b1 = false; if((b1==true) && ((output+=10)==20)){ System.out.println("We are equal "+output); }else { System.out.println("Not equal! "+output); } //代码段2 int output=10; boolean b1 = false; if((b1==true) & ((output+=10)==20)){ System.out.println("We are equal "+output); }else { System.out.println("Not equal! "+output); } 等价于:output=output+10
  • 41. 字符串操作符 例题:which of the following are valid? String s1= " Hello"+ "World"; //s1= "HelloWorld" String s2="Age:"+’1’; //s2= "Age:1" String s3="Age:"+new Integer(1); //s3= "Age:1" String s4="Answer :" +true; //s4= "Answer:true" String s5="Answer :" +new Boolean("true"); //s5= "Answer:true" String s6=5+1+ "1" +new Integer(1)+ 2 +4+ new Long(11); String s7=5+new Integer(1)+ "2" +‘4’+ new Long(11); //编译出错 System.out.println(5+1+"1"+new Integer(1)+ 2 +4+ new Long(11)); //打印6112411 以下表达式的执行步骤如下: (1)5+1 →6 //数学加法操作符 (2)6+"1" →"61" //字符串连接操作符 (3)"61"+new Integer(1) →"611" //字符串连接操作符 (4)"611"+2 →"6112" //字符串连接操作符 (5)"6112"+4 →"61124" //字符串连接操作符 (6)"61124"+new Long(11) →"6112411" //字符串连接操作符
  • 42. 操作符==int a=1; int b=1; Integer int1=new Integer(1); Integer int2=new Integer(1); Integer int3=int1; //int3和int1引用同一个对象 int[] array1=new int[1]; int[] array2=new int[1]; int[] array3=array1; //array3和array1引用同一个数组 System.out.println(" a==b is "+(a==b)); System.out.println("int1==int2 is "+( int1==int2)); System.out.println("int1==int3 is "+( int1==int3)); System.out.println("array1==array2 is "+(array1==array2)); System.out.println("array1==array3 is "+(array1==array3)); 程序打印结果如下: A==b is true int1==int2 is false int1==int3 is true array1==array2 is false array1==array3 is true 当操作符==两边都是引用类型变量时,这两个引用变量必须都引用同一 个对象,结果才为true。堆区Integer对象Integer对象int1变量 int3变量 int2变量
  • 43. equals()方法equals()方法是在Object类中定义的方法,它的声明格式如下: public boolean equals(Object obj) Object类的equals()方法的比较规则为:当参数obj引用的对象与当前对象为同一个对象,就返回true,否则返回false: public boolean equals(Object obj){ if(this==obj)return true; else return false; } Animal animal1=new Dog(); Animal animal2=new Cat(); Animal animal3=animal1; System.out.println(animal1 == animal2); //打印false System.out.println(animal1.equals(animal2)); //打印false System.out.println(animal1 == animal3); //打印true System.out.println(animal1.equals(animal3)); //打印true
  • 44. equals()方法在JDK中有一些类覆盖了Object类的equals()方法,它们的比较规则为:如果两个对象的类型一致,并且内容一致,则返回true。这些类包括:java.io.File、java.util.Date、java.lang.String、包装类(如java.lang.Integer和java.lang.Double类)。Integer int1=new Integer(1); Integer int2=new Integer(1); String str1=new String("Hello"); String str2=new String("Hello"); System.out.println(int1==int2); //打印false System.out.println(int1.equals(int2)); //打印true System.out.println(str1==str2); //打印false System.out.println(str1.equals(str2)); //打印true
  • 45. 区分==和equals()方法 例题:Which of the following yields a boolean value of true.Float f1 = new Float("10F"); Float f2 = new Float("10F"); Double d1 = new Double("10D"); a) f1 == f2 b) f1.equals(f2) c) f2.equals(d1) d) f2.equals(new Float("10"))
  • 46. 基本类型安全转换规则目标数据类型的位数不小于被转换的数据的数据类的位数,这样才是类型安全的转换,才不会丢失需要转换的信息。 基本数据类型   →  目标类型 byte→ short→ char→ int→long→float→ double 高位转化为低位,必须进行强制类型转化 short和char之间的赋值总需要一个强制类型转化 byte b=1; int i=b; short s=(short)i; char c=(char)s;int a=256; byte b=(byte)a; //数据溢出,变量b的值为0
  • 47. 基本类型安全转换规则 例题: Which of the following lines will compile without warning or errora) float f=1.3; b) char c="a"; c) byte b=257; d) boolean b=null; e) int i=10; f) short s=1; char cs=s;
  • 48. 引用类型的类型转换规则子类赋值给父类,会自动进行类型转换。 父类赋值给子类,需要进行强制类型转换。 一个生物类型的引用变量可以引用任意一个其子类的实例。 一个动物类型的引用变量永远不能引用植物类型的实例。 一个动物类型的引用变量赋值给狗类型的引用变量,需要强制类型转换。 Animal animal; Cat cat; Dog dog; animal=cat; //编译成功 cat=animal; //编译出错 cat=(Cat)animal; //编译成功 dog=(Dog)cat; //编译出错
  • 49. 引用类型的类型转换规则 例题:What will happen if you attempt to compile and run the following code? class Base {} class Sub extends Base {} public class Tester{ public static void main(String argv[]){ Base b=new Sub(); Sub s= b; } } a) Compile and run without error b) Compile time Exception c) Runtime Exception Sub s=(Sub)b;
  • 50. 参数传递如果参数是基本数据类型,参数传递是将参数的数值传递给方法。 如果参数是对象或数组,参数传递是将对象或数组的引用传递给方法。 public void methodA(){ int a=1; String s=“Hello” methodB(a,s); } public void methodB(int p1,String p2){ … }
  • 51. ParaTest.javapublic class ParaTest{ public int v=0; public static void main(String args[]){ //声明并初始化4个局部变量 int p1=0; // p1是基本数据类型 ParaTest p2=new ParaTest(); //p2 是对象引用类型 ParaTest p3=new ParaTest();//p3 是对象引用类型 int[] p4={0}; //p4 是数组引用类型 //将4个局部变量作为参数传递给changeParameter ()方法 changeParameter (p1, p2, p3, p4); //打印4个局部变量 System.out.println("p1= " +p1); System.out.println("p2.v= " + p2.v); System.out.println("p3.v= " + p3.v); System.out.println("p4[0]= " +p4[0]); } public static void changeParameter (int p1, ParaTest p2, ParaTest p3, int[] p4) { p1=1; //改变基本数据类型参数的值 p2.v=1; //改变对象类型参数的实例变量 p3=new ParaTest(); //改变对象类型参数的引用,使它引用一个新的对象 p3.v=1; //改变新的对象的实例变量 p4[0]=1; //改变数组类型参数的元素 } } 打印: P1=0 P2.v=1 P3.v=0 P4[0]=1
  • 52. ParaTest.javamain()changeParameter()int p1=0ParaTest p2ParaTest p3int[] p4int p1=0ParaTest p2ParaTest p3int[] p4调用ParaTest对象 v=0 ParaTest对象 v=0当main方法将参数传递给changeParameter(),两个方法中的p2,p3都分别引用同一个ParaTest实例int数组 int[0]=0
  • 53. ParaTest.javamain()changeParameter ()int p1=0ParaTest p2ParaTest p3int[] p4int p1=1ParaTest p2ParaTest p3int[] p4调用ParaTest对象 v=1ParaTest对象 v=0在changeParameter方法中, p3引用了一个新的ParaTest实例ParaTest对象 v=1int数组 int[0]=1
  • 54. 流程控制分支语句 if-else语句 switch语句 循环语句 for语句 while语句 do-while语句 特殊循环流程控制 continue break
  • 55. 分支语句(if, else语句)下面的语句是非法的: int x=1; if (x) // x is int 你应该使用下列语句替代: int x=1; if (x ! = 0) public void amethod(int x){ if(x>0){ System.out.println("大于0"); }else if(x==0){ System.out.println("等于0"); }else if(x<0){ System.out.println("小于0"); } }
  • 56. 分支语句(switch语句) int x=9; final int Y=2; switch(x){// 必须是与int类型赋值兼容的变量 default: System.out.println(“default”); case 1: System.out.println(“case1”); case Y: System.out.println(“caseY”); } int x=1; final int Y=2; switch(x){// 必须是与int类型赋值兼容的变量 default: System.out.println(“default”); case 1: System.out.println(“case1”); break; case Y: System.out.println(“caseY”); } 打印 default case1 caseY打印 case1
  • 57. 循环语句//do-while循环 int i=1; do{ System.out.println(i); }while(i++<3); /while循环 int i=0; while(i++<3) System.out.println(i); } //for循环 for(int i=1;i<=3;i++) System.out.println(i); 打印 1 2 3
  • 58. 流程控制语句break:从switch语句、循环语句或标号标识的代码块中退出。以下while循环用于把1加到100: int a=1,result=0; one: while(true){ result+=a++; if(a==101)break one; //终止循环 } System.out.println(result); //打印5050 continue:跳过本次循环,执行下一次循环,或执行标号标识的循环体。以下for循环用于对1到100之内的奇数求和: int result=0; for(int a=1;a<=100;a++){ if(a%2==0)continue; //如果a是偶数,就跳出本次循环,继续执行下次循环 else result+=a; } System.out.println(result); //打印2500
  • 59. 第4课 对象和类构造方法 重载方法 覆盖方法 访问控制 static变量,方法和初始代码块 this引用的用途 final类,方法和变量 abstract类和方法,接口 解释如何以及何时使用内部类 降级,以及如何把Java程序从JDK的低版本升级到高版本 参考《Java面向对象编程》的第6、7、8和12章interface MyIFC{ void method1(); void method1(int a); } abstract class Base{ public void method1(){ System.out.println("hi"); } protected abstract void method2(); } class Sub extends Base implements MyIFC{ private int a; private static int b; public static final int C=1; Sub(){this(-1);} Sub(int a){this.a=a;} public void method1(){a++;} public void method1(int a){this.a=a;} public void method2(){a--;} public static void method3(){b++;} }
  • 60. 构造方法声明构造方法的语法规则 重载构造方法,参见Employee.java 默认构造方法, 参见Sample1.java 子类调用父类的构造方法,参见Son.java,
  • 61. 构造方法的语法规则一个新对象的初始化的最终步骤是去调用对象的构造方法。 构造方法必须满足以下条件: 方法名必须与类名称完全相匹配; 不要声明返回类型; 不能被static、final、synchronized、abstract、native修饰。 public class Sample { int x; public Sample() { // No-arg constructor x=1; } public Sample(int x) { //int-arg constructor this.x=x; } }
  • 62. 构造方法的语法规则public class Sample { int x; public void Sample() { x=1; } public static void main(String args[]){ Sample s=new Sample(); System.out.println(s.x); } }
  • 63. 重载构造方法public class Employee { private String name; private int salary; public Employee(String n, int s) { name = n; salary = s; } public Employee(String n) { this(n, 0); } public Employee() { this( " Unknown " ); } }Employee tom=new Employee("Tom",1000); Employee jack=new Employee("Jack"); Employee someone=new Employee();
  • 64. 默认构造方法默认构造方法是没有参数的构造方法,你可以显式定义类的默认构造方法。 为了保证每个类至少有一个构造方法,如果定义的类中一个构造方法也没有写,Java将自动提供一个默认构造方法。该构造方法没有参数,用public 修饰,而且方法体为空。格式如下: public ClassName(){} 只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去默认构造方法。
  • 65. 默认构造方法public class Sample1{} public class Sample2{ public Sample2(int a){System.out.println(“My Constructor”);} }   public class Sample3{ public Sample3(){System.out.println(“My Default Constructor”);} } Sample1 s1=new Sample1(); Sample2 s2=new Sample2(); //非法 Sample2 s22=new Sample2(1); Sample3 s3=new Sample3();
  • 66. 在构造子类对象时,JVM会先调用父类的构造方法 子类构造方法中通过super语句调用父类构造方法 如果子类构造方法中没有通过super语句调用父类构造方法,那么JVM会调用父类的默认构造方法,如果不存在默认构造方法,将导致编译错误 子类调用父类构造方法
  • 67. class Father{ String fatherName; Father(){ this.fatherName=“未知"; } Father(String fatherName){ this.fatherName=fatherName; } } class Son extends Father{ String sonName; Son(String sonName){ this.sonName=sonName; } Son(String sonName,String fatherName){ super(fatherName); this.sonName=sonName; } } 子类调用父类构造方法Son son1=new Son("王小毛","王大毛"); Son son2=new Son("张三"); System.out.println(son1.sonName); System.out.println(son1.fatherName); System.out.println(son2.sonName); System.out.println(son2.fatherName);
  • 68. 修饰符修饰符的类型 访问控制修饰符(public,protected,private) static,abstract,final 修饰符的修饰内容(类,方法,变量) 修饰符的作用 使用修饰符的限制
  • 69. 成员变量或成员方法的访问控制 修饰符 同类 同包 子类 不同包 public 是 是 是 是 protected 是 是 是 默认 是 是 private 是
  • 70. 成员变量或成员方法的访问控制包1包2public int v1; protected int v2; int v3 private int v4;ClassAClassBClassCClassD extends ClassAClassB, ClassC,ClassD分别可以访问ClassA的哪些成员变量?
  • 71. 类的访问控制顶层类只能是public或默认访问级别 public级别的类可以被同一个包或者其他包中的类访问 默认级别的类只能被同一个包中的类访问 public class Sample{…} //public级别 class Sample{…} //默认访问级别 protected class Sample{…} //非法 private class Sample{…} //非法
  • 72. static关键字类(static)变量,参见Count.java 类(static)方法,参见Wrong.java 静态初始化程序,参见StaticBlock.java
  • 73. 静态变量和实例变量静态变量在装载类的时候被分配内存并初始化,类只能被装载一次,所以静态变量在内存中只有一个拷贝 实例变量在创建实例时被分配内存并初始化,所以每个实例都有各自的实例变量 同一个类的实例之间共享静态变量
  • 74. 静态变量和实例变量public class Count { private int serialNumber; private static int counter; public Count() { counter++; serialNumber = counter; System.out.println("My serialNumber is " + serialNumber); } public static void main(String args[]){ System.out.println("At first,counter="+ counter); Count count1=new Count(); System.out.println("after creat count1, counter="+counter); Count count2=new Count(); System.out.println("At last counter="+counter);   System.out.println("count1.serialNumber"+count1.serialNumber); System.out.println("count2.serialNumber"+count2.serialNumber); System.out.println("count1.counter"+count1.counter); System.out.println("count2.counter"+count2.counter); System.out.println("Count.counter"+Count.counter); } }
  • 75. 静态变量和实例变量堆区Count对象serialNumber=1方法区Count的类型信息counter=1count1引用变量堆区Count对象serialNumber=1方法区Count的类型信息counter=2count1引用变量count2引用变量Count对象serialNumber=2Count count1=new Count();Count count2=new Count();Count.counter//合法 Count.serialNumber//非法
  • 76. 静态方法和实例方法成员方法分为类方法和实例方法。用static修饰的方法叫类方法,或静态方法。 静态方法也和静态变量一样,不需创建类的实例,可以直接通过类名被访问。 static方法不能被修饰成protected和abstract。public class GeneralFunction { public static int addUp(int x, int y) { return x + y; } } public class UseGeneral { public void method() { int a = 9; int b = 10; int c = GeneralFunction.addUp(a, b); System.out.println("addUp() gives " + c); } }
  • 77. 静态方法和实例方法public class Wrong { int x; void method(){x++;} public static void test() { x = 1; //非法 method();//非法 } public static void main(String args[]) { x = 9; //非法 method();//非法 } }堆区Wrong对象 实例变量xWrong对象 实例变量xWrong.test() ?静态方法中不允许直接访问实例变量和实例方法
  • 78. 静态方法和实例方法public class Correct{ int x; void method(){ x++; //合法 } public static void main(String args[]) { Correct r1=new Correct(); r1.x = 9; // 合法 r1.method();// 合法 Correct r2=new Correct(); r2.x = 10; // 合法 r2.method();// 合法 System.out.println(r1.x); System.out.println(r2.x); } }堆区Correct对象 实例变量xCorrect对象 实例变量x引用变量r1引用变量r2
  • 79. this关键字this关键字引用当前实例 在static方法中不能使用this关键字public class Sample{ int x; Sample(int x){ this.x=x; method(this); } void method(Sample s){ s.x++; //合法 } public static void test() { this.x++; //非法 } }Sample s1=new Sample(1); Sample s2=new Sample(2); System.out.println(s1.x); System.out.println(s2.x);堆区Sample对象 实例变量xSample对象 实例变量x引用变量s1引用变量s2
  • 80. 静态初始化程序 类中可以包含静态代码块,它不存在于任何方法体中。当类被装载时,静态代码块只被执行一次。类中不同的静态块按它们在类中出现的顺序被依次执行。 public class Sample{ static int i = 5; static { System.out.println(" First Static code i= "+ i++ ); } static { System.out.println(" Second Static code i= "+ i++ ); } public static void main(String args[]) { Sample s1=new Sample(); Sample s2=new Sample(); System.out.println("At last, i= "+ i ); } }打印 First Static code i=5 Second Static code i=6 At last,i=7
  • 81. final关键字final类:不能被继承 final方法: 不能被子类覆盖 final变量:必须被显式的初始化,并且只能初始化一次,参见InitFinal0.java public final class A{} public class B extends A{} //非法public class A{ public final int method(){ return 1; } } public class B extends A{ public int method(){ //非法 return 2; } }
  • 82. final变量 例:What will happen when compile the following code?public class Test{ final int x = 0; Test(){ x = 1; //非法 } final int aMethod(){ return x; } }
  • 83. final变量 例:What will happen when compile the following code?1. class FinalTest{ 2. final int q; 3. 4. FinalTest(){ 5. this(0); 6. q = 1; } 7. 8. FinalTest(int x){ 9. q = x; 10. } 11. }FinalTest f=new FinalTest();
  • 84. final变量 例:What will happen when compile the following code?1. class FinalTest{ 2. final int q; 3. 4. FinalTest(){} 5. 6. FinalTest(int x){ 7. q = x; 8. } 9. }FinalTest f=new FinalTest();
  • 85. native关键字native只用来修饰方法。 native方法用其它语言(如C语言)实现,所以没有程序代码块。 public static native int myNativeMethod(int p);
  • 86. extends关键字和类的继承继承是复用程序代码的有力手段,当多个类(Sub1,Sub2…Sub100)之间存在相同的属性和方法,可从这些类中抽象出父类Base,在父类Base中定义这些相同的属性和方法,所有的Sub类无需重新定义这些属性和方法,只需通过extends语句来声明继承Base类: public class Sub extends Base{…} Sub类就会自动拥有在Base类中定义的属性和方法。 Java中不支持多继承 public class ClassC extends ClassA,ClassB{} //非法
  • 87. abstract关键字abstract修饰符可用来修饰类和成员方法: 用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化,即不允许创建抽象类本身的实例。没有用abstract修饰的类称为具体类,具体类可以被实例化。 用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。没有用abstract修饰的方法称为具体方法,具体方法具有方法体。 public abstract class Base{ //Base是抽象类 abstract void method1(); //抽象方法 void method2(){ //具体方法 System.out.println("method2"); } }
  • 88. abstract关键字一个类中有抽象方法,这个类必须是抽象类 抽象类中可以没有抽象方法 abstract class Base{ abstract void method1(); abstract void method2(); } class Sub extends Base{ //编译出错,Sub类必须声明为抽象类 void method1(){System.out.println("method1");} }
  • 89. 接口在Java语言中,接口有两种意思: 一是指概念性的接口,即指系统对外提供的所有服务。类的所有能被外部使用者访问的方法构成了类的接口。 二是指用interface关键字定义的实实在在的接口,也称为接口类型。它用于明确的描述系统对外提供的所有服务,它能够更加清晰的把系统的实现细节与接口分离。 public interface Transparency { public static final int OPAQUE=1; public static final int BITMASK=2; public static final int TRANSLUCENT=3; public int getTransparency(); }
  • 90. 接口的特征接口中只能包含public、static、final类型的成员变量和public、abstract类型的成员方法。 接口中不能有非抽象方法 public interface A{ int var; //编译出错 void method1(){System.out.println("method1");} //编译出错 protected void method2(); //编译出错 static void method3(){System.out.println("method3");} //编译出错 }
  • 91. 接口的特征接口之间允许存在继承关系 interface A{ void method1(); } interface B{ void method2(); } interface D extends A,B{} //合法 interface E implements A,B{} //错误
  • 92. 接口的实现接口由类来实现 一个类能实现许多接口。 public class MyApplet extends Applet implements Runnable, MouseListener{ }
  • 93. 接口的实现 interface SayHello { void printMessage(); } class SayHelloImpl implements SayHello { public void printMessage() { System.out.println("Hello"); } }
  • 94. 接口的实现 interface SayHello { void printMessage(); void receiveMessage(); } abstract class SayHelloImpl implements SayHello { public void printMessage() { System.out.println("Hello"); } }
  • 95. 方法重载(overload)对于类的方法(包括从父类中继承的方法),如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法是另一个方法的重载方法。 重载方法必须满足以下条件: 方法名相同。 方法的参数类型、个数、顺序至少有一项不相同。 方法的返回类型可以不相同。 方法的修饰符可以不相同。 //java.lang.Math类的用于取最大值的max方法, //有多个重载方法。 public static int max(int a,int b) public static long max(long a,long b) public static float max(float a,float b) public static double max(double a,double b)int a=Math.max(1,2); double d=Math.max(1,2.0);
  • 96. 方法重载(overload)以下Sample类中已经定义了一个amethod()方法: public class Sample{ public void amethod(int i, String s){} //加入其他方法 } 下面哪些方法可以加入到Sample类中,并且保证编译正确呢? A)public void amethod(String s, int i){} //可以 B)public int amethod(int i, String s){return 0;} //不可以 C)private void amethod(int i, String mystring){} //不可以 D)public void Amethod(int i, String s) {} //可以 E)abstract void amethod(int i); //不可以
  • 97. 方法覆盖(override)方法覆盖是指子类重新实现了父类中的方法 以下代码中子类覆盖了父类的一个方法,然后定义了一个重载方法: public class Parent { public void method() {System.out.println(“Parent”); } } public class Child extends Parent { public void method(){System.out.println(“Child”);} //override public int method(int a) { //overload return 0; } }
  • 98. 方法覆盖的约束条件子类方法的名称、参数签名和返回类型必须与父类方法的名称、参数签名和返回类型一致。 public class Base { public void method() {…} } public class Sub extends Base{ public int method() { //编译错误,返回类型不一致 return 0; } }
  • 99. 方法覆盖的约束条件子类方法不能缩小父类方法的访问权限,但可以扩大访问权限 public class Parent { public void method() { } } public class Child extends Parent { private void method() { //编译错误 } }
  • 100. 方法覆盖的约束条件子类方法不能抛出比父类方法更多的异常 class ExceptionA extends Exception{} class ExceptionB extends ExceptionA{} class ExceptionC extends ExceptionB{} public class Parent { void method() throws ExceptionB{} } public class Child1 extends Parent { void method()throws ExceptionA {} //非法 } public class Child2 extends Parent { void method()throws ExceptionC {} //合法 }
  • 101. 多态性Java语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换: Animal animal=new Dog(); Dog dog=(Dog)animal; //向下转型,把Animal类型转换为Dog类型 Creature creature=animal; //向上转型,把Animal类型转换为Creature类型 animal=new Cat(); 如果把引用变量转换为子类类型,称为向下转型,如果把引用变量转换为父类类型,称为向上转型。 在进行引用变量的类型转换时,会受到各种限制。而且在通过引用变量访问它所引用的实例的静态属性、静态方法、实例属性、实例方法,以及从父类中继承的方法和属性时,Java虚拟机会采用不同的绑定机制。
  • 102. 多态性(参见Tester.java)class Fathers{ String var="FatherVar"; static String staticVar="StaticFatherVar"; void method(){System.out.println("Father method");} static void staticMethod(){System.out.println("Static Father method");} }class Sons extends Fathers{ String var="SonVar"; static String staticVar="StaticSonVar"; void method(){System.out.println("Son method");} static void staticMethod(){System.out.println("Static Son method");} String sonVar=null; void sonMethod(){} }
  • 103. 多态性(参见Tester.java)public class Tester{ public void test(){ Fathers f=new Sons(); //Fathers f=new Fathers(); //Sons f=new Sons(); //Sons f=(Sons)new Fathers(); System.out.println(f.var); System.out.println(f.staticVar); f.method(); f.staticMethod(); } public static void main(String args[]){ new Tester().test(); } }
  • 104. 多态性参见Tester.java 对于一个引用类型的变量,编译器按照它声明的类型处理。 例如以下代码编译出错。 Fathers f=new Sons(); f.sonVar=“123”; f.sonMethod(); 如果要访问Sons的成员,必须通过强制转换: ((Sons)f).sonVar=“123”; ((Sons)f).sonMethod(); Fathersvar method() staticVar staticMethod()Sonsvar method() staticVar staticMethod() sonVar sonMethod()
  • 105. 多态性 对于一个引用类型的变量,运行时按照它实际引用的对象处理。 例如以下代码虽然编译可以通过,但运行时会抛出ClassCastException。 Fathersvar method() staticVar staticMethod()Sonsvar method() staticVar staticMethod() sonVar sonMethod()Fathers f=new Fathers(); Sons s=(Sons)f;//throw exception when runAnimal a=new Dog(); Cat c=(Cat)a;//throw exception when run
  • 106. 多态性 成员变量、静态方法按照引用变量声明的类型静态绑定;实例方法按照引用变量引用的实例动态绑定 例如,对于以下这段代码: Fathers f=new Sons(); System.out.println(“f.var=”+f.var); System.out.println(“f.staticVar=”+f.staticVar); f.method(); f.staticMethod(); 运行时将会输出如下结果: f.var=FatherVar f.staticVar=StaticFaterVar Son method Static Father methodFathersvar method() staticVar staticMethod()Sonsvar method() staticVar staticMethod() sonVar sonMethod()
  • 107. 例:What will be written to the standard output when the following program is run? (参见 PolyTester.java)参见PolyTester.java class Base { int i; Base() {add(1);} void add(int v) { i+= v; } void print() {System.out.println(i);} } class Extension extends Base { Extension(){add(2);} void add(int v) { i+= v*2; } }public class PolyTester { public static void main(String args[]) { bogo(new Extension()); } static void bogo(Base b) { b.add(8); b.print(); } }
  • 108. 例:What will be written to the standard output when the following program is run? (参见 PolyTester.java)以上代码创建的是Extension类的实例,所以在运行时,所有调用add()方法的过程,将始终和Extension类的add()方法动态绑定。 以下是程序依次对变量i的改变过程: 初试值:i=0 step1:创建实例new Extension() 先调用父类的默认构造方法Base(),父类的默认构造方法中执行add(1),i=0+1*2 → i=2,再调用子类的默认构造方法Extension(),子类的默认构造方法中执行add(2),i=2+2*2→i=6。 step2:执行add(8) i=6+8*2→i=22   本例子考察知识点: 方法动态绑定 子类调用父类的构造方法
  • 109. 内部类内部类,是在一个类的内部定义的类。 匿名类是一种特殊的内部类。 内部类的特性 内部类的实例化,参见InnerInit.java
  • 110. 内部类interface OuterIFC{ void method();} public class Outer{ public class Inner1{} //实例内部类 public static class Inner2{} //静态内部类 private OuterIFC o=new OuterIFC(){ int i=1; void method(){i++;} }; //一个实现了OuterIFC接口的匿名类 OuterIFC getInstance(){return o;} void outerMethod(){ class Inner3{}; //局部内部类 Inner3 in=new Inner3(); } }//end of class Outer class Tester{ public void test(){ Outer.Inner1 in1=new Outer().new Inner1(); Outer.Inner1 in11=new Outer.Inner1(); //非法 Outer.Inner2 in2=new Outer().new Inner2(); Outer.Inner2 in22=new Outer.Inner2(); } }
  • 111. 内部类的特性实例内部类可以直接访问嵌套类的成员。如果是定义在方法中的局部内部类,还可以访问该方法中的final型的局部变量和final型的方法参数。参见ClassA.java class ClassA{ private int vA=0; class B{int vB=vA; } void methodA(final int param1,int param2){ final int localV=0; class C{ int vC; void methodC() { int v1C=vA; //合法 int v2C=localV; //合法 int v3C=param1; //合法 // int v4C=param2; //非法 } }//end of class C }//end of methodA }//end of class A
  • 112. 内部类的特性静态内部类只能直接访问所嵌套类的静态成员,而不能直接访问所嵌套类的非静态的成员,如果一定要访问,必须通过外部嵌套类的实例访问。 class A{ int vA=1; static class B{ int vB=vA; //非法 } } class C{ int vC=1; static class D{ int vD=new C().vC; //合法 } }
  • 113. 内部类的特性实例内部类不能声明任何static成员;静态内部类中可以声明static成员。 class A{ class B{ static int var; //非法 } } class C{ static class D{ static int v; //合法 static class E{} //合法 } } C.D.v=1; C.D.E e=new C.D.E();
  • 114. 降级(deprecation)在版本升级时,有些类、类的构造方法或成员方法被淘汰,这成为降级。
  • 115. 降级(deprecation)当升级代码时,用-deprecation标志来编译代码: javac -deprecation XXXXjava 例如编译DateConverter.java
  • 116. instanceof 运算符instanceof运算符的使用形式如下: obj instanceof ClassName 或者 obj instanceof InterfaceName instanceof运算符的使用方法 void testType(Animal o){ if(o instanceof Dog){System.out.println(“dog”);} if(o instanceof Cat){System.out.println(“Cat”);} } void test(){ Animal o1=new Dog(); testType(o1); o1=new Cat(); testType(o1); }
  • 117. instanceof 运算符引用变量a声明为Animal类型,如果以下代码为true,引用变量a可能引用哪些类的实例? (a instanceof Animal) && !(a instanceof Cat)CreatureAnimalCatDogCock
  • 118. 第5课 异常处理Java异常处理机制 把各种不同类型的异常情况进行分类,用Java类来表示异常情况,这种类被称为异常类。把异常情况表示成异常类,可以充分发挥类的可扩展和可重用的优势。 异常流程的代码和正常流程的代码分离,提高了程序的可读性,简化了程序的结构。 可以灵活的处理异常,如果当前方法有能力处理异常,就捕获并处理它,否则只需抛出异常,由方法调用者来处理它。参见《Java面向对象编程》的第9章public void work() { try{ 工作8个小时 //可能会抛出DiseaseException异常 下班回家 }catch(DiseaseException e){ 去医院看病 } }
  • 119. 异常处理在Java编程语言中,用try和catch语句来处理异常。格式如下: 1. try { 2. // code that might throw a particular exception 3. } catch (SpecialException e) { 4. // code to execute if a SpecialException is thrown 5. } catch (Exception e) { 6. // code to execute if a general Exception exception is thrown 7. }
  • 120. 异常处理如果一个方法不想处理异常,可以通过throws 语句将异常抛向上级调用方法。 int method1(int x)throws Exception1,Exception2 { if(x<0)throw new Exception1(); if(x==0)throw new Exception2(); return ++x; } void method2() throws Exception1,Exception2 { //以下代码可能抛出异常 int a=method1(1); }
  • 121. 异常处理采用堆栈机制public class ExTester{ static int method1(int x)throws Exception{ if(x<0)throw new Exception("x<0"); return ++x; } static int method2(int x)throws Exception{ return method1(x); } public static void main(String args[])throws Exception{ System.out.println(method2(-1)); } }main()method2()method1()方法调用堆栈
  • 122. finally语句finally语句定义一个总是被执行的代码块,而不管有没有出现异常。public void work() { try{ 开门 工作8个小时 //可能会抛出DiseaseException异常 }catch(DiseaseException e){ 去医院看病; }finally{ 关门 } }public void work() { try{ 开门 工作8个小时//可能会抛出DiseaseException异常 关门 }catch(DiseaseException e){ 去医院看病; } }
  • 123. finally语句finally语句定义一个总是被执行的代码块,而不考虑是否出现异常。 public class FinallyTester{ static int method1(int x)throws Exception{ if(x<0)throw new Exception("x<0"); return x++; } public static void main(String args[]){ try{ System.out.println(method1(-1)); System.out.println("end"); }catch(Exception e){ System.out.println("Wrong"); }finally{ System.out.println("Finally"); }
  • 124. 异常处理流程try{ code1; //可能抛出各种异常 }catch(SQLException e){ System.out.println("SQLException"); }catch(IOException e){ System.out.println("IOException"); }catch(Exception e){ System.out.println("Exception"); }
  • 125. 异常处理流程finally语句不被执行的唯一情况是程序先执行了终止程序的System.exit()方法 public static void main(String args[]){ try{ System.out.println("Begin"); System.exit(0); }finally{ System.out.println("Finally"); } System.out.println("End"); }
  • 126. 异常处理流程public static void main(String args[])throws Exception{ try{ System.out.println("Begin"); new Sample().method1(-1); //出现SpecialException异常 System.exit(0); }catch(Exception e){ System.out.println("Wrong"); throw e; //如果把此行注释掉,将得到不同的运行结果 }finally{ System.out.println("Finally"); } System.out.println("End"); }打印 Begin   Wrong   Finally java.lang.SpecialException
  • 127. 异常处理流程public static int method1(int x){ try{ if(x<0)throw new Exception("x<0"); return x++; }catch(Exception e){ System.out.println(“Wrong"); return -100; }finally{ System.out.println("Finally"); } } public static void main(String args[]){ System.out.println( method1(-1)); }打印 Wrong   Finally -100
  • 128. JDK类库中的异常分类
  • 129. 常见异常ArithmeticException:数学异常 int a=12 / 0; //抛出ArithmeticException NullPointerException:空指针异常 Date d= null; System.out.println(d.toString()); //抛出NullPointerException ArrayIndexOutOfBoundsException:下标越界异常 int[] array=new int[4]; array[0]=1; array[7]=1; //抛出ArrayIndexOutOfBoundsException ClassCastException:类型转换异常: Animal animal=new Dog(); Cat cat=(Animal)animal;
  • 130. 运行时异常 RuntimeException类及其子类都称为运行时异常,这种异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。例如当以下divide()方法的参数b为0,执行“a/b”操作时会出现ArrithmeticException异常,它属于运行时异常,Java编译器不会检查它: public int divide(int a,int b){ return a/b; //当参数b为0,抛出ArrithmeticException }
  • 131. 区分运行时异常和受检查异常运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误操作。一旦出现了错误操作,建议终止程序,因此Java编译器不检查这种异常。 public void method(int[] array){ for(int i=0;i<=array.length;i++) array[i]=1; //当i的取值为array.length时,将抛出ArrayIndexOutOfBoundsException } public void method(int[] array){ for(int i=0;i
  • 132. 区分运行时异常和受检查异常受检查异常表示程序可以处理的异常,如果抛出异常的方法本身不能处理它,那么方法调用者应该去处理它,从而使程序恢复运行,不至于终止程序。 如果一个方法可能出现受检查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。 void method1() throws IOException{} //合法 //编译错误,必须捕获或声明抛出IOException void method2(){ method1(); } //合法,声明抛出IOException void method3()throws IOException { method1(); }//合法,声明抛出Exception void method4()throws Exception { method1(); } //合法,捕获IOException void method5(){ try{ method1(); }catch(IOException e){…} }
  • 133. 用户定义异常用户定义异常是通过扩展Exception类或RuntimeException来创建的。 class AnswerWrongException extends Exception { private int result; public AnswerWrongException (int result){ this.result=result; } public int getResult() { return result; } }
  • 134. 用户定义异常(参见ExceptionTester.java)public class ExceptionTester{ public static void test(int x,int y,int z)throws AnswerWrongException{ if(x+y!=z) throw new AnswerWrongException(z); System.out.println(x+"+"+y+"="+z); } public static void main(String args[]) { try{ test(1,2,5); System.out.println("end"); }catch( AnswerWrongException e){ System.out.println("result is wrong:"+e.getResult()); e.printStackTrace(); } } }
  • 135. 获得异常信息 Exception提供了如下方法: toString() getMessage() printStackTrace()try{ test(1,2,5); System.out.println("end"); }catch( AnswerWrongException e){ e.printStackTrace(); }
  • 136. 第6课 多线程线程的概念 创建和启动线程 线程的调度 多个线程的同步 线程之间的通信 参见《Java面向对象编程》的13章
  • 137. 线程的概念进程是指运行中的应用程序,每一个进程都有自己独立的内存空间。对一个应用程序可以同时启动多个进程。例如每次执行JDK的java.exe程序,就启动了一个独立的Java虚拟机进程,该进程的任务是解析并执行Java程序代码。 线程是指进程中的一个执行流程,有时也称为执行情景。一个进程可以由多个线程组成,即在一个进程中可以同时运行多个不同的线程,它们分别执行不同的任务。当进程内的多个线程同时运行,这种运行方式称为并发运行。
  • 138. 主线程每当用java命令启动一个Java虚拟机进程,Java虚拟机就会创建一个主线程,该线程从程序入口main()方法开始执行。 public class Sample{ public void method1(String str){ System.out.println(str); } public void method2(String str){ method1(str); } public static void main(String args[]){ Sample s=new Sample(); s.method2("hello"); } }main()方法method1()方法method2()方法主线程的方法调用栈
  • 139. 线程的创建和启动(1)定义一个Thread类的子类,覆盖Thread类的run()方法,然后创建该子类的实例。 参见MyThread .java (2)定义一个实现Runnable接口的类,实现它的run()方法,然后将这个类的实例作为Thread的构造方法的参数,创建Thread类的实例。 参见MyRunnable .java (3)调用start()方法启动线程。比如, MyThread t = new MyThread(); t.start();
  • 140. 创建Thread类的子类public class MyThread extends Thread{ public void run(){ for(int a=0;a<50;a++){ try{Thread.sleep(100);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+" "+a); } } public static void main(String args[]){ MyThread t1=new MyThread(); MyThread t2=new MyThread(); t1.start(); t2.start(); } }
  • 141. 区分主线程和用户定义的线程public class MyThread extends Thread{ public void run(){ for(int a=0;a<50;a++){ try{Thread.sleep(100);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+" "+a); } } public static void main(String args[]){ MyThread t1=new MyThread(); t1.start(); t1.run(); } }
  • 142. 实现Runnable接口public class MyRunnable implements Runnable{ int a=0; //实例变量 public void run(){ for( a=0;a<10;a++){ try{Thread.sleep(100);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+" "+a); } } public static void main(String args[]){ MyRunnable mr=new MyRunnable (); Thread t1=new Thread(mr); //Thread(Runnable r) Thread t2=new Thread(mr); t1.start(); t2.start(); } }
  • 143. 实现Runnable接口public static void main(String args[]){ MyRunnable mr=new MyRunnable (); Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.start(); t2.start(); } public static void main(String args[]){ MyRunnable mr1=new MyRunnable (); MyRunnable mr2=new MyRunnable (); Thread t1=new Thread(mr1); Thread t2=new Thread(mr2); t1.start(); t2.start(); }
  • 144. 线程状态转换
  • 145. 线程状态转换Stack1对象push()pop()getName()producer1consumer1producer2producer3runningblockedrunnableblockedStack2对象push()pop()getName()producer4consumer2runnableblocked
  • 146. 线程调度 因为Java线程的调度不一定是分时的,所以你必须确保你的代码中的线程会不时地给另外一个线程运行的机会。有三种方法可以做到一点: 让处于运行状态的线程调用Thread.sleep()方法。 让处于运行状态的线程调用Thread.yield()方法。 让处于运行状态的线程调用另一个线程的join()方法。
  • 147. 线程睡眠:Thread.sleep()方法public class MyThread extends Thread{ public void run(){ for(int a=0;a<50;a++){ try{Thread.sleep(100);}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+" "+a); } } public static void main(String args[]){ MyThread t1=new MyThread(); MyThread t2=new MyThread(); t1.start(); t2.start(); } }
  • 148. 线程让步:Thead.yield()方法public class MyThread extends Thread{ public void run(){ for(int a=0;a<50;a++){ yield(); System.out.println(Thread.currentThread().getName()+" "+a); } } public static void main(String args[]){ MyThread t1=new MyThread(); MyThread t2=new MyThread(); t1.start(); t2.start(); } }
  • 149. 等待其他线程结束:join()public class Machine extends Thread{ public void run(){ for(int a=0;a<10;a++) System.out.println(getName()+":"+a); } public static void main(String args[])throws Exception{ Machine machine=new Machine(); machine.setName("m1"); machine.start(); System.out.println("main:join machine"); machine.join(); //主线程等待machine线程运行结束 System.out.println("main:end"); } }
  • 150. 共享资源的竞争当多个线程共享一些数据,它们的操作会竞争共享资源,这会导致一些意想不到的错误。 以一个生产者(Producer),消费者(Consumer)为例,生产者向一个堆栈(SyncStack )中加入产品,字符消费者向同一个堆栈中取出产品。 SyncTest类:提供程序入口main()方法,负责创建生产者和消费者线 程,并且启动这些线程。 Producer类:生产者线程,不断向堆栈中加入产品。 Consumer类:消费者线程,不断向堆栈中取出产品。 Stack类:堆栈,允许从堆栈中取出或加入产品。 参见mythread\problem\SyncTest.java
  • 151. 堆栈Stack.javaclass Stack { private String name; private String[] buffer=new String[100]; int point=-1;   …   public String pop() { String goods = buffer[point]; buffer[point]=null; Thread.yield(); point--; return goods; }   public void push(String goods) { point++; Thread.yield(); buffer[point]=goods; } buffer[0]buffer1] buffer[2] buffer[3] push()pop()point
  • 152. 生产者线程class Producer extends Thread { private Stack theStack; public Producer (Stack s,String name) { super(name); theStack = s; start(); //启动自身生产者线程 } public void run() { String goods; for (int i = 0; i < 200; i++) { goods="goods"+(theStack.getPoint()+1); theStack.push(goods); System.out.println(getName()+ ": push " + goods +" to "+theStack.getName()); yield(); } } } buffer[0]=goods0buffer1] =goods1 buffer[2] =goods2 buffer[3] =goods3 point生产者线程
  • 153. 消费者线程class Consumer extends Thread { private Stack theStack; public Consumer (Stack s,String name) { super(name); theStack = s; start(); //启动自身消费者线程 } public void run() { String goods; for (int i=0; i < 200; i++) { goods = theStack.pop(); System.out.println(getName() + ": pop " + goods +" from "+theStack.getName()); yield(); } } } buffer[0]=goods0buffer1] =goods1 buffer[2] =goods2 buffer[3] =goods3 point消费者线程
  • 154. 创建生产者和消费者线程public class SyncTest { public static void main(String args[]) { Stack stack = new Stack("stack1"); Producer producer1 = new Producer(stack,"producer1"); Consumer consumer1 = new Consumer(stack,"consumer1"); } } 堆栈生产者消费者
  • 155. 共享资源的竞争打印结果: producer1: push goods0 to stack1 consumer1: pop null from stack1 producer1: push goods0 to stack1 …
  • 156. 线程的同步同步是一种保证共享资源完整性的手段 具体作法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码块。 public String pop() { synchronized(this){ String goods = buffer[point]; buffer[point]=null; Thread.yield(); point--; return goods; } } public synchronized String pop() { String goods = buffer[point]; buffer[point]=null; Thread.yield(); point--; return goods; }
  • 157. 对操纵堆栈的方法同步class Stack { … public synchronized int getPoint(){return point;} public synchronized String pop() {…} public synchronized void push(String goods) {…} }
  • 158. 对操纵堆栈的方法同步每个Java对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。当消费者线程试图执行带有synchronized标记的代码块pop()方法,消费者线程必须首先获得Stack对象的锁。在以下两种情况,消费者线程有着不同的命运: 假如这个锁已经被其他线程占用,Java虚拟机就会把这个消费者线程放到Stack对象的锁池中,消费者线程进入阻塞状态。在Stack对象的锁池中可能会有许多等待锁的线程。等到其他线程释放了锁,Java虚拟机会从锁池中随机的取出一个线程,使这个线程拥有锁,并且转到就绪状态。 假如这个锁没有被其他线程占用,消费者线程就会获得这把锁,开始执行同步代码块。一般情况下,消费者线程只有执行完同步代码块,才会释放锁,使得其他线程能够获得锁,当然也有一些例外情况。比如当线程执行同步代码块时出现中断或异常而跳出synchronized代码块,锁也会自动释放。
  • 159. 线程的同步的特点一把锁可以锁住多个同步代码块 锁对非同步代码块无效 当一个线程进入同步代码块,并不意味着指定代码必须以不中断的方式运行。 当一个线程占有了某个对象的锁,其他需要获得这个锁的线程就进入锁池中,等待获得锁的机会。
  • 160. 线程的同步的特点public class SyncTest { public static void main(String args[]) { Stack stack1 = new Stack("stack1"); Producer producer1 = new Producer(stack1,"producer1"); Producer producer2 = new Producer(stack1,"producer2"); Producer producer3 = new Producer(stack1,"producer3"); Consumer consumer1 = new Consumer(stack1,"consumer1"); Stack stack2 = new Stack("stack2"); Producer producer4 = new Producer(stack2,"producer4"); Consumer consumer2 = new Consumer(stack2,"consumer2"); } }
  • 161. 线程的同步的特点Stack1对象push()pop()getName()producer1consumer1producer2producer3runningblockedrunnableblockedStack2对象push()pop()getName()producer4consumer2runnableblocked
  • 162. 死锁当一个线程等待由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。 Java不监测也不试图避免这种情况。因而保证不发生死锁就成了程序员的责任。 参见DeadLockTest.java
  • 163. 死锁class Operator implements Runnable{ Operator anotherOperator; synchronized public void methodA(){ System.out.println(Thread.currentThread().getName()+":begin methodA"); try{Thread.sleep(200);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+":call another methodA"); anotherOperator.methodA(); System.out.println(Thread.currentThread().getName()+":end methodA"); } public void run(){ methodA(); } }
  • 164. 死锁public class DeadLockTest { public static void main(String args[]){ Operator o1=new Operator(); Operator o2=new Operator(); o1.anotherOperator=o2; o2.anotherOperator=o1; Thread t1=new Thread(o1); Thread t2=new Thread(o2); t1.start(); t2.start(); } } Operator1methodA()t1Operator2methodA()t2
  • 165. 线程通信不同的线程执行不同的任务,如果这些任务有某种联系,线程之间必须能够通信,协调完成工作。例如生产者和消费者共同操作堆栈,当堆栈为空时,消费者无法取出产品,应该先通知生产者向堆栈中加入产品。当堆栈已满时,生产者无法继续加入产品,应该先通知消费者从堆栈中取出产品。 java.lang.Object类中提供了两个用于线程通信的方法: wait():执行该方法的线程释放对象的锁,Java虚拟机把该线程放到该对象的等待池中。该线程等待其他线程将它唤醒。 notify():执行该方法的线程唤醒在对象的等待池中等待的一个线程。Java虚拟机从对象的等待池中随机的选择一个线程,把它转到对象的锁池中。
  • 166. 线程通信假定线程t1和线程t2共同操纵一个对象s,这两个线程可以通过对象s的wait()和notify()方法来进行的通信。 S 对象method1()t1t2method2()
  • 167. 生产者与消费者线程通信class Stack { … public synchronized String pop() { this.notifyAll(); while(point==-1){ System.out.println(Thread.currentThread().getName()+": wait"); try{ this.wait(); }catch(InterruptedException e){throw new RuntimeException(e);} } String goods = buffer[point]; buffer[point]=null; Thread.yield(); point--; return goods; } public synchronized void push(String goods) { …} }参见mythread\newproblem\SyncTest.java
  • 168. 生产者与消费者线程通信class Stack { … public synchronized void push(String goods) { this.notifyAll(); while(point==buffer.length-1){ System.out.println(Thread.currentThread().getName()+": wait"); try{ this.wait(); }catch(InterruptedException e){throw new RuntimeException(e);} } point++; Thread.yield(); buffer[point]=goods; } }
  • 169. 生产者与消费者线程通信public class SyncTest { public static void main(String args[]) { Stack stack1 = new Stack("stack1"); Producer producer1 = new Producer(stack1,"producer1"); Consumer consumer1 = new Consumer(stack1,"consumer1"); Consumer consumer2 = new Consumer(stack1,"consumer2"); } }
  • 170. 生产者与消费者线程通信对于stack1,同时有两个消费者取出产品,只有一个生产者加入产品,因此有可能导致消费者取产品时堆栈为空的情况。以下是consumer2线程取产品时可能出现的流程。 (1)执行this.notifyAll()方法,此时this引用的stack1对象的等待池中没有任何线程,因此该方法什么也不做。 (2)由于point为-1,因此执行this.wait()方法,consumer2线程释放stack1对象的锁,并且进入stack1对象的等待池。 (3)producer1线程获得stack1对象的锁,开始执行push()方法。 (4)producer1线程首先执行this.notifyAll()方法,此时this引用的stack1对象的等待池中有一个consumer2线程,因此把这个线程转到stack1对象的锁池。 (5)producer1线程判断point不为buffer.length-1,无需执行this.wait()方法,producer1线程向堆栈中加入一个产品,然后退出push()方法,并且释放锁。 (6)在stack1对象的锁池中的consumer2线程获得了锁,转到就绪状态,只要获得了CPU,就能继续执行pop()方法。
  • 171. 终止线程当线程执行完run()方法,它将自然终止运行。 Thread有一个stop()方法,可以强制结束线程,但这种方法是不安全的。因此,在JDK1.2开始的版本中,stop()方法已经被废弃。 实际编程中,一般是定义一个标志变量,然后通过程序来改变标志变量的值,从而控制线程从run()方法中自然退出。 参见MyThreadStop.java
  • 172. 终止线程public class MyThreadStop extends Thread{ int a; boolean flag=false; public void run(){ while(!flag){ System.out.println(a++); } } public void setFlag(boolean _flag){ flag=_flag; } public static void main(String args[]){ MyThreadStop t=new MyThreadStop(); t.start(); try{ Thread.sleep(1000);}catch(Exception e){} t.setFlag(true); } }
  • 173. Thread类的方法currentThread() 返回当前运行的Thread对象。 start() 启动一个线程。 run() 线程体,由start()方法调用,当run()方法返回时,当前的线程结束。 stop() 使调用它的线程立即停止执行。 sleep(int n)使线程睡眠n毫秒,n毫秒后,线程可以再次运行。 suspend() 使线程挂起,暂停运行。 resume() 恢复挂起的线程,使其处于可运行状态(Runnable)。 yield() 将CPU控制权主动移交到下一个可运行线程。 setPriority() 设置线程优先级。 getPriority() 返回线程优先级。 setName() 设置线程的名字。 getName() 返回该线程的名字。 isAlive( ) 如果线程已被启动并且未被终止,那么isAlive( )返回true。如果返回false,则该线程是新创建或是已被终止的。
  • 174. 第7课 输入流和输出流流是程序和外界进行数据交换的通道 分为输入流(InputStream)和输出流(OutputStream)。程序通过输入流从数据源读取数据,通过输出流向目的地写数据。 参见《Java面向对象编程》的第16章
  • 175. 输入流InputStream类是所有输入流的父类,它是一个抽象类,不能被实例化。它提供了一系列和读取数据有关的方法。 int read(), int read(byte[] b) 从数据源读取数据 void close() 当完成读操作后,应该关闭输入流。
  • 176. 输出流OutputStream类是所有输出流的父类,它是一个抽象类,不能被实例化。它提供了一系列和写数据有关的方法。 void write(int),void write(byte[] b) 向输出流写数据。 void close() 当完成写操作后,应该关闭输出流。 void flush() OutputStream类本身的flush()方法不执行任何操作,它的一些带有缓冲区的子类(比如BufferedOutputStream和PrintStream类)覆盖了flush()方法。通过带缓冲区的输出流写数据时,数据先保存在缓冲区中,积累到一定程度才会真正写到输出流中。缓冲区通常用字节数组实现,实际上是指一块内存空间。flush()方法强制把缓冲区内的数据写到输出流中。
  • 177. 输入流的层次
  • 178. 基本的输入流类
  • 179. FileInputStreamimport java.io.*; class FileReaderSample { public static void main(String agrs[])throws IOException{ FileInputStream in=new FileInputStream(“C:\\in.txt"); int data; while((data=in.read())!=-1) System.out.print(data +" "); in.close(); } }
  • 180. 过滤输入流:FilterInputStream FilterInputStream是一种用于扩展输入流功能的装饰器,它有好几个子类,分别用来扩展输入流的某一种功能
  • 181. DataInputStream类DataInputStream实现了DataInput接口,用于读取基本类型数据,如int、float、long、double和boolean等。此外,DataInputStream的readUTF()方法还能读取采用UTF-8编码的字符串。DataInputStream类的所有读方法都都以“read”开头,比如: readByte():从输入流中读取1个字节,把它转换为byte类型的数据。 readLong():从输入流中读取8个字节,把它转换为long类型的数据。 readFloat():从输入流中读取4个字节,把它转换为float类型的数据。 readUTF():从输入流中读取1到3个字节,把它转换为采用UTF-8编码的字符串。
  • 182. DataInputStream类 (参见FormatDataIO.java) FileOutputStream out1=new FileOutputStream("D:\\test.txt"); BufferedOutputStream out2=new BufferedOutputStream(out1); //装饰文件输出流 DataOutputStream out=new DataOutputStream(out2); //装饰带缓冲输出流 out.writeByte(-12); out.writeLong(12); out.writeChar('1'); out.writeUTF("好"); out.close(); InputStream in1=new FileInputStream("D:\\test.txt"); BufferedInputStream in2=new BufferedInputStream(in1); //装饰文件输入流 DataInputStream in=new DataInputStream(in2); //装饰缓冲输入流 System.out.print(in.readByte()+" "); System.out.print(in.readLong()+" "); System.out.print(in.readChar()+" "); System.out.print(in.readUTF()+" "); in.close();
  • 183. 输出流的层次
  • 184. FileOutputStreamFileOutputStream向文件写数据,它有以下构造方法: FileOutputStream(File file) FileOutputStream(String name) FileOutputStream(String name, boolean append) 在创建FileOutputStream实例时,如果相应的文件并不存在,会自动创建一个空的文件。如果参数file或name表示的文件路径尽管存在,但是代表一个文件目录,那么会抛出FileNotFoundException异常。 默认情况下,FileOutputStream向文件写数据时,将覆盖文件中原有的内容。以上第四个构造方法提供了一个布尔类型的参数append,如果append参数为true,将在文件末尾添加数据。
  • 185. FileOutputStreamimport java.io.*; public class FileWriterSample{ public static void main(String args[]) { try { FileOutputStream fileOut = new FileOutputStream("C:\\out.txt"); fileOut.write("你好".getBytes()); fileOut.close(); } catch(FileNotFoundException e) { } catch(IOException e) { } } }
  • 186. URL输入流(java.net.URL) InputStream is = null; try { URL url= new URL("http://www.javathinker.org/weiqin/sole.htm"); is = url.openStream(); byte buffer[] = new byte[2048]; is.read(buffer,0,buffer.length); System.out.println(new String(buffer)); } catch (IOException e){} //参见URLTest.java
  • 187. 读者和作者InputStream和OutputStream类处理的是字节流,也就是说,数据流中的最小单元为一个字节,它包括8个二进制位。 在许多应用场合,Java程序需要读写文本文件。在文件文件中存放了采用特定字符编码的字符。为了便于读写采用各种字符编码的字符,java.io包中提供了Reader/Writer类,它们分别表示字符输入流和字符输出流。 Reader和Writer能够实现本地平台的字符编码和Java程序可以处理的Unicode编码的自动转换。
  • 188. 读者和作者默认情况下,如果你构造了一个连接到流的Reader和Writer,那么转换规则会对本地平台默认的字符编码和Unicode进行转换。 在构造InputStreamReader类和OutputStreamWriter类的实例时,你可以指定输出流或输入流的字符编码。 InputStreamReader reader=new InputStreamReader(new FileInputStream("D:\\test.txt"), "UTF-8" ); char c=(char)reader.read();
  • 189. 关于字符编码系统属性file.encoding表示本地平台的字符编码,参见PropertityTester.java,中文系统一般是GBK String的构造方法String(byte[] bytes, String enc)可以按指定的字符编码构造字符串 String的方法byte[]getBytes(String enc) 获得按指定的字符编码的字节数组
  • 190. 获得平地平台的字符编码 (参见PropertityTester.java) Properties p=System.getProperties(); Enumeration k=p.keys(); Enumeration e=p.elements(); String userName= (String)p.get("user.name"); while(k.hasMoreElements()){ System.out.println(k.nextElement()+ " :"+ e.nextElement()); }
  • 191. 关于字符编码参见Encoder.java String s="你好!"; byte[] gbk=s.getBytes("GBK"); byte[] utf=s.getBytes(“UTF8"); printByteArray(gbk); //-60 -29 -70 -61 -93 -95 printByteArray(utf);//-28 -67 -96 -27 -91 -67 -17 -68 -127 String gbk_string=new String(gbk,"GBK"); String utf_string=new String(utf,“UTF8");
  • 192. 读者和作者
  • 193. 读者和作者InputStreamReader BfferedReader OutputStreamWriter PrintWriter
  • 194. BufferedReaderReader类的read()方法每次都从数据源读入一个字符,为了提高效率,可以采用BufferedReader来装饰其他Reader。 BufferedReader带有缓冲区,它可以先把一批数据读到缓冲区内,接下来的读操作都是从缓冲区内获取数据,避免每次都从数据源读取数据并进行字符编码转换,从而提高读操作的效率。 BufferedReader的readLine()方法可以一次读入一行字符,以字符串形式返回。BufferedReader类有两个构造方法: BufferedReader(Reader in) :参数in指定被装饰的Reader类。 BufferedReader(Reader in, int sz) :参数in指定被装饰的Reader类,参数sz指定缓冲区的大小,以字符为单位。
  • 195. PrintWriterPrintWriter能输出格式化的数据,PrintWriter的写数据的方法都以print开头,比如: print(int i):向输出流写入一个int类型的数据。 print(long l): 向输出流写入一个long类型的数据。 print(float f): 向输出流写入一个float类型的数据。 print(String s): 向输出流写入一个String类型的数据。 println(int i): 向输出流写入一个int类型的数据和换行符。 println(long l): 向输出流写入一个long类型的数据和换行符。 println(float f): 向输出流写入一个float类型的数据和换行符。 println(String s): 向输出流写入一个String类型的数据和换行符println(“hello”); 等价于: print(“hello”) print(“\n”)
  • 196. 读者和作者(练习)从文件in.txt中读取内容,再将它写到文件out.txt中。in.txt采用本地平台默认的字符编码,而out.txt分别采用和本地平台不同的字符编码和本地平台相同的字符编码。 OutputStreamWriter ow=new OutputStreamWriter(fo,"UTF8"); OutputStreamWriter ow=new OutputStreamWriter(fo); OutputStreamWriter ow=new OutputStreamWriter(fo,"UTF16"); 参见MyReaderWriter.java
  • 197. MyReaderWriter.java public void copyfile(String from, String to)throws Exception{ FileInputStream fin=new FileInputStream(from); InputStreamReader ir=new InputStreamReader(fin); BufferedReader in=new BufferedReader(ir); FileOutputStream fo=new FileOutputStream(to); OutputStreamWriter ow=new OutputStreamWriter(fo,“UTF8"); //OutputStreamWriter ow=new OutputStreamWriter(fo,"GBK"); BufferedWriter bw =new BufferedWriter(ow); PrintWriter out=new PrintWriter(bw,true); String data=in.readLine(); while(data!=null){ out.println(data); data=in.readLine(); } in.close(); out.close(); } public static void main(String [] args)throws Exception{ new MyReaderWriter().copyfile("C:\\in.txt","C:\\out.txt"); }
  • 198. File类File类提供了若干处理文件、目录和获取它们基本信息的方法。 File 类的构造方法有三个: 1. File(String pathname) 2. File(String parent, String child) 3. File(File parent, String child)
  • 199. File类的作用 察看文件属性:canRead(),isFile(),lastModified() 创建或删除目录:mkdir(),delete() 列出目录下的所有文件:list() 判断文件是否存在:exists() 重新命名文件:renameTo()
  • 200. File类的作用(参见UseFile.java)File dir1=new File("D:\\dir1"); if(!dir1.exists())dir1.mkdir(); File dir2=new File(dir1,"dir2"); if(!dir2.exists())dir2.mkdirs(); File dir4=new File(dir1,"dir3\\dir4"); if(!dir4.exists())dir4.mkdirs(); File file=new File(dir2,"test.txt"); if(!file.exists())file.createNewFile();创建文件系统
  • 201. File类的作用(参见UseFile.java)public static void listDir(File dir){ File[] lists=dir.listFiles(); //打印当前目录下包含的所有子目录和文件的名字 String info="目录:"+dir.getName()+"("; for(int i=0;i
  • 202. File类的作用(参见UseFile.java)/** 删除目录或文件,如果参数file代表目录,会删除当前目录以及目录下的所有内容*/ public static void deleteDir(File file){ //如果file代表文件,就删除该文件 if(file.isFile()){ file.delete(); return; } //如果file代表目录,先删除目录下的所有子目录和文件 File[] lists=file.listFiles(); for(int i=0;i
  • 203. RandomAccessFile类RandomAccessFile类用来随机读取和写入文件(即可以写入文件中任何一个地方)。 RandomAccessFile实现了DataInput和DataOutput 接口。 RandomAccessFile类提供了定位文件的方法
  • 204. RandomTester.javaimport java.io.*; public class RandomTester { public static void main(String args[])throws IOException{ RandomAccessFile rf=new RandomAccessFile("D:\\test.dat","rw"); for(int i=0;i<10;i++) rf.writeLong(i*1000); rf.seek(5*8); //从文件开头开始,跳过第5个long数据,接下来写第6个long数据 rf.writeLong(1234); rf.seek(0); //把读写指针定位到文件开头 for(int i=0;i<10;i++) System.out.println("Value"+i+":"+rf.readLong()); rf.close(); } Value0:0 Value1:1000 Value2:2000 Value3:3000 Value4:4000 Value5:1234 Value6:6000 Value7:7000 Value8:8000 Value9:9000
  • 205. 第8课 java.lang 包 java.lang包包含了主要的Java语言支持类, 它是Java语言编程设计的基础。 作为Java语言中所有类的祖先Object类,就定义在Java.lang包中. 另外还包含了包装类、String类、多线程支持类、Math类、System类等。 参考《Java面向对象编程》的第19章
  • 206. Object 类Object 类是所有Java类的最终祖先,如果一个类在声明时没有包含extends关键词,那么这个类直接继承Object类。 (1)equals(Object obj) (2)notify() (3)nofityAll() (4)wait() (5)toString():返回当前对象的字符串表示,格式为“类名@对象的16进制哈希码”。许多类,如String、StringBuffer和包装类都覆盖了toString()方法,返回具有实际意义的内容。
  • 207. Object 类的toString()方法当System.out.println()方法的参数为Object类型,println()方法会自动先调用Object对象的toString()方法,然后打印toString()方法返回的字符串。 System.out.println(new Object().toString()); //打印java.lang.Object@273d3c System.out.println(new Integer(100).toString()); //打印100 System.out.println(new String("123").toString()); //打印123 System.out.println(new StringBuffer("123456").toString()); //打印123456 以上代码等价于: System.out.println(new Object()); System.out.println(new Integer(100)); System.out.println(new String("123")); System.out.println(new StringBuffer("123456"));
  • 208. String类indexOf()和lastIndexOf():在字符串中检索特定字符或子字符串,indexOf()方法从字符串的首位开始查找,而lastIndexOf()方法从字符串的末尾开始查找。如果找到,则返回匹配成功的位置,如果没有找到,则返回-1。String str="HelloHelloHello"; //查找字符'e'第一次在str中出现的位置 System.out.println(str.indexOf('e')); //打印1 //查找字符'e'从位置2开始第一次在str中出现的位置 System.out.println(str.indexOf('e',2)); //打印6 //查找字符'e'在str中最后一次出现的位置 System.out.println(str.lastIndexOf('e')); //打印11 //查找字符串"ello"从位置2开始第一次在str中出现的位置 System.out.println(str.indexOf("ello",2)); //打印6 //查找字符串"Ello"第一次在str中出现的位置 System.out.println(str.indexOf("Ello")); //打印-1
  • 209. String类concat(String str):把字符串str附加在当前字符串的末尾。例如: String str="Hello"; String newStr=str.concat("World"); System.out.println(str); //打印Hello System.out.println(newStr); //打印HelloWorld 以上concat()方法并不会改变字符串str本身的内容。
  • 210. String类substring():返回字符串的一个子字符串,有以下两种重载形式: public String substring(int beginIndex) public String substring(int beginIndex, int endIndex) 子串在源串中的起始位置为beginIndex,结束位置为endIndex-1。如果没有提供endIndex参数,那么结束位置为:字符串长度-1。例如: String str="0123456"; String sub1=str.substring(2); String sub2=str.substring(2,5); System.out.println(str); //打印0123456 System.out.println(sub1); //打印23456 System.out.println(sub2); //打印234
  • 211. StringBuffer类length():返回字符串的字符个数,与String类的length()用法相同。 append():向缓冲区内添加新的字符串,例如: StringBuffer sb=new StringBuffer(); sb.append("Hello"); sb.append("World"); System.out.println(sb); //打印HelloWorld substring():用法与String类的substring()方法相同。 insert( int offset, String str):在字符串中的offset位置插入字符串str,例如: StringBuffer sb=new StringBuffer("0456"); sb.insert(1,"123"); System.out.println(sb); //打印0123456
  • 212. 比较String类和StringBuffer类String类不可编辑 StringBuffer类可编辑 String s1=new String(“abc”); s1.concat(“def”); StringBuffer sb1=new StringBuffer(“abc”); sb1.append(“def”); System.out.println(s1); System.out.println(sb1);String对象 (abc)String对象 (abcdef)s1.concat(“def”)s1引用变量StringBuffer对象 (abcdef)sb1.append(“def”)sb1引用变量
  • 213. 包装类 基本类型的数据分别对应的包装类为: boolean Boolean byte Byte char Charactor short Short int Integer long Long float Float double Double
  • 214. 包装类的层次结构Number类的主要方法如下: byteValue():返回Number对象所表示的数字的byte类型值。 intValue():返回Number对象所表示的数字的int类型值。 longValue():返回Number对象所表示的数字的long类型值。 shortValue():返回Number对象所表示的数字的short类型值。 doubleValue():返回Number对象所表示的数字的double类型值。 floatValue():返回Number对象所表示的数字的float类型值。
  • 215. 包装类的构造方法Integer i=new Integer(1); Float f=new Float( 1.0f); Double d=new Double(1.0); Integer i=new Integer("123"); Double d=new Double("123.45D"); Float f=new Float("123.45F");
  • 216. 包装类的常用方法除Character类和Boolean类以外,包装类都有valueOf(String s)静态工厂方法,可以根据String类型的参数来创建包装类对象。参数字符串s不能为null,而且该字符串必须可以解析为相应的基本类型的数据,否则虽然编译会通过,运行时会抛出NumberFormatException。例如: Double d=Double.valueOf("123"); //合法 Integer i=Integer.valuesOf("12"); //合法 Integer i=Integer.valuesOf("abc"); //运行时抛出NumberFormatException
  • 217. 包装类的常用方法除Character类和Boolean类以外,包装类都有parseXXX(String str)静态方法,把字符串转变为相应的基本类型的数据(XXX表示基本类型的名称)。参数str不能为null,而且该字符串必须可以解析为相应的基本类型的数据,否则虽然编译会通过,运行时会抛出NumberFormatException。例如: int i=Integer.parseInt("123"); //合法,i=123 double d=Double.parseDouble("abc"); //抛出NumberFormatException
  • 218. 包装类的用法举例将字符串转换为某种基本类型。 int a=Integer.parseInt(“123”); int a1=new Integer(“123”).intValue(); double d=Double.parseDouble(“234.567”); double d1=new Double (“234.567”).doubleValue();
  • 219. Math类 Math类是final的。 Math的构造方法是private的。 Math提供的方法都是静态的。
  • 220. Math类的主要方法abs():返回绝对值。 ceil():返回大于等于参数的最小整数。 floor():返回小于等于参数的最大整数。 max():返回两个参数的较大值。 min():返回两个参数的较小值。 random():返回0.0和1.0 之间的double类型的随机数,包括0.0,但不包括1.0。 round():返回四舍五入的整数值。 sin():正弦函数。 cos():余弦函数。 tan():正切函数。 exp():返回自然对数的幂。 sqrt():平方根函数。 pow():幂运算。
  • 221. Math类的常用方法的参数类型和返回类型
  • 222. Math类的常用方法System.out.println(Math.round(3.3)); //打印3 System.out.println(Math.round(-3.3)); //打印-3 System.out.println(Math.ceil(3.3)); //打印4.0 System.out.println(Math.ceil(-3.3)); //打印-3.0 System.out.println(Math.floor(3.3)); //打印3.0 System.out.println(Math.floor(-3.3)); //打印-4.0 int a=Math.floor(3.3); //编译出错
  • 223. 第9课 Java集合(位于java.util包中)Java集合主要分为三种类型: Set(集):集合中的对象不按特定方式排序,并且没有重复对象。它的有些实现类能对集合中对象按特定方式排序。 List(列表):集合中的对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象。List与数组有些相似。 Map(映射):集合中的每一个元素包含一对键对象和值对象,集合中没有重复的键对象,值对象可以重复。它的有些实现类能对集合中的键对象进行排序。 参考《Java面向对象编程》的第15章
  • 224. Java集合与数组的区别在创建Java数组时,必须明确指定数组的长度,数组一旦创建,其长度就不能被改变。为了使程序能方便的存储和操纵数目不固定的一组数据,JDK类库提供了Java集合,所有Java集合类都位于java.util包中。 与Java数组不同,Java集合中不能存放基本类型数据,而只能存放对象的引用。
  • 225. 主要集合类的类框图
  • 226. Set(集)Set是最简单的一种集合,集合中的对象不按特定方式排序,并且没有重复对象。 Set接口主要有两个实现类HashSet和TreeSet。 HashSet类按照哈希算法来存取集合中的对象,存取速度比较快。HashSet类还有一个子类LinkedHashSet类,它不仅实现了哈希算法,而且实现了链表数据结构,链表数据结构能提高插入和删除元素的性能。 TreeSet类实现了SortedSet接口,具有排序功能。
  • 227. Set的一般用法Set set=new HashSet(); String s1=new String("hello"); String s2=s1; String s3=new String("world"); set.add(s1); set.add(s2); set.add(s3); System.out.println(set.size()); //打印集合中对象的数目2
  • 228. List(列表)List的主要特征是其元素以线性方式存储,集合中允许存放重复对象。List接口主要的实现类包括: ArrayList:ArrayList代表长度可变的数组。允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素的速度较慢。 LinkedList:在实现中采用链表数据结构。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问则相对较慢。随机访问是指检索位于特定索引位置的元素。LinkedList单独具有addFirst()、addLast()、getFirst()、getLast()、removeFirst()和removeLast()方法,这些方法使得LinkedList可以作为堆栈、队列和双向队列使用。
  • 229. List的用法向列表中加入元素 List list=new ArrayList(); list.add(new Integer(3)); list.add(new Integer(4)); list.add(new Integer(3)); list.add(new Integer(2)); List的get(int index)方法返回集合中由参数index指定的索引位置的对象,第一个加入到集合中的对象的索引位置为0。以下程序依次检索出集合中的所有对象: for(int i=0;i
  • 230. 对List中的元素进行自然排序 List list=new ArrayList(); list.add(new Integer(3)); list.add(new Integer(4)); list.add(new Integer(3)); list.add(new Integer(2));   Collections.sort(list); for(int i=0;i
  • 231. Map(映射)Map(映射)是一种把键对象和值对象进行映射的集合,它的每一个元素都包含一对键对象和值对象。 向Map集合中加入元素时,必须提供一对键对象和值对象,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。 接口Map提供了根据关键字找对应的值的方法get(),定义如下: Object get(Object key) 接口Map还提供了设置关键字和对应值的方法put(),定义如下: Object put(Object key,Object value)
  • 232. Map的用法以下程序通过Map的put(Object key,Object value)方法向集合中加入元素,通过Map的get(Object key)方法来检索与键对象对应的值对象: Map map=new HashMap(); map.put("1","Monday"); map.put("2","Tuesday"); map.put("3","Wendsday"); map.put("4","Thursday"); String day=map.get("2"); //day的值为“Tuesday”
  • 233. 第10课 用户界面设计参见《Java面向对象编程》的第17,18章
  • 234. AWT容器容器Container能够用来存放别的组件。 有两种类型的容器:Window和Panel。
  • 235. Frame容器Window是能独立存在的容器,它有一个子类Frame。 Frame有一个构造方法Frame(String title) 你可以通过add()方法,在Frame中加入其他的组件。 Frame被创建后,它是不可见的. 参见FrameShower.java
  • 236. FrameShower.javapackage gui; import java.awt.*; public class FrameShower{ public static void main(String args[]){ Frame f=new Frame("hello"); f.add(new Button("Press Me")); f.setSize(100,100); f.setVisible(true); } }
  • 237. Panel容器 Panel只能存在于其他的容器(Window或其子类)中. 通过Panel的默认构造方法Panel()可以创建一个Panel。 参见MyFrame.java
  • 238. MyFrame.javapackage gui; import java.awt.*; public class MyFrame extends Frame{ Panel panel=new Panel(); Button button=new Button("press me"); public MyFrame(String title){ super(title); panel.add(button); panel.setBackground(Color.yellow); add(panel); setBackground(Color.blue); setSize(500,500); setVisible(true); } public static void main(String args[]){ MyFrame f=new MyFrame("hello"); } }
  • 239. 布局管理器一.取消布局管理器 setLayout(null) 二.默认布局管理器 Window,Frame和Dialog的默认布局管理器是BorderLayout Panel和Applet的默认布局管理器是FlowLayout。
  • 240. 取消布局管理器public MyFrame(String title){ super(title); panel.setLayout(null); panel.setSize(200,200); panel.setLocation(50,50); button.setSize(80,50); button.setLocation(80,80); panel.add(button); panel.setBackground(Color.yellow); setLayout(null); add(panel); setBackground(Color.blue); setSize(500,500); setVisible(true); }yx
  • 241. 布局管理器布局管理器分为5种: - FlowLayout 流式布局管理器 - BorderLayout边界布局管理器 - GridLayout网格布局管理器 - CardLayout卡片布局管理器 - GridBagLayout网格包布局管理器
  • 242. 布局管理器练习运行 MyFlow.java 运行 BorderLayoutTester.java 运行GridEx.java 运行 CardLayoutTester.java 运行GridBagEx1.java 改变容器中组件的布局,观看显示效果
  • 243. FlowLayoutpublic class MyFlow { private Frame f; private Button button1, button2, button3; public static void main (String args[]) { MyFlow mflow = new MyFlow (); mflow.go(); } public void go() { f = new Frame ("Flow Layout"); f.setLayout(new FlowLayout()); button1 = new Button("Ok"); button2 = new Button("Open"); button3 = new Button("Close"); f.add(button1); f.add(button2); f.add(button3); f.setSize (600,600); f.setVisible(true); } }
  • 244. BorderLayoutpublic class BorderLayoutTester { private Frame f; private Button bn, bs, bw, be, bc; public static void main(String args[]) { BorderLayoutTester guiWindow2 = new BorderLayoutTester(); guiWindow2.go(); } public void go() { f = new Frame("Border Layout"); //f.setLayout(new BorderLayout()); bn = new Button("B1"); bs = new Button("B2"); be = new Button("B3"); bw = new Button("B4"); bc = new Button("B5"); f.add(bn, BorderLayout.NORTH); f.add(bs, BorderLayout.SOUTH); f.add(be, BorderLayout.EAST); f.add(bw, BorderLayout.WEST); f.add(bc, BorderLayout.CENTER); f.add(new Button("hello")); f.pack(); f.setVisible(true); } }
  • 245. GridLayoutpublic class GridEx { private Frame f; private Button b1, b2, b3, b4, b5, b6; public static void main(String args[]) { GridEx grid = new GridEx(); grid.go(); } public void go() { f = new Frame("Grid example"); f.setLayout (new GridLayout (3, 2)); b1 = new Button("1"); b2 = new Button("2"); b3 = new Button("3"); b4 = new Button("4"); b5 = new Button("5"); b6 = new Button("6"); f.add(b1); f.add(b2); f.add(b3); f.add(b4); f.add(b5); f.add(b6); f.setSize(500,500); f.setVisible(true); } }
  • 246. CardLayoutpublic class CardLayoutTester { public static void main(String args[]){ Panel p1, p2, p3; Frame f = new Frame ("Card Test"); CardLayout myCard = new CardLayout(); f.setLayout(myCard); p1 = new Panel();p2 = new Panel(); p3 = new Panel(); f.setBackground(Color.white); p1.setBackground(Color.black); p2.setBackground(Color.blue); p3.setBackground(Color.red); f.add(p1, "First"); f.add(p2, "Second"); f.add(p3, "Third"); myCard.show(f, "Second"); f.setSize (200, 200); f.setVisible(true); } }
  • 247. 创建面板及复杂布局参见 ExGui3.java
  • 248. 创建面板及复杂布局参见ExGui4.java
  • 249. 事件处理每一个可以触发事件的组件被当作事件源. 每一种事件都对应专门的监听者。 监听者用来接收和处理这种事件。 一个事件源可以触发多种事件,如果它注册了某种事件对应的监听者,那么这种事件就会被接收和处理。这种模式被称为 "委托模型"。
  • 250. 事件处理的软件实现事件类(XXXEvent) 事件监听接口(XXXListener) 组件的注册监听接口方法(addXXXListener()方法)
  • 251. 事件处理1.用内部类实现监听接口 参看EventTester1.java 2.将容器类实现监听接口 参看EventTester2.java 3.定义专门的外部类实现监听接口 参看EventTester3.java 4.采用事件适配器 参看EventTester4.java 5.一个组件注册多个监听者 参看EventTester5.java
  • 252. 用内部类实现监听接口public class EventTester1 extends Frame{ static int count=1; public EventTester1 (String title){super(title);} public static void main(String args[]){ EventTester1 f=new EventTester1 ("hello"); f.setLayout(new FlowLayout()); final Button b=new Button("1"); b.addActionListener(new ActionListener(){ //declare an Inner class public void actionPerformed(ActionEvent evt){ b.setLabel(new Integer(++count).toString()); } }); f.add(b); f.setSize(100,100); f.setBackground(Color.blue); f.setVisible(true); } }
  • 253. 将容器类实现监听接口public class EventTester2 extends Frame implements ActionListener{ int count=1; Button b; public EventTester2(String title){ super(title); setLayout(new FlowLayout()); b=new Button("1"); b.addActionListener(this); //Sample itself is an ActionListener add(b); setSize(100,100); setBackground(Color.blue); setVisible(true); } public static void main(String args[]){ EventTester2 f=new EventTester2 ("hello"); } public void actionPerformed(ActionEvent evt){ b.setLabel(new Integer(++count).toString()); } }
  • 254. 定义专门的外部类实现监听接口public class EventTester3 extends Frame { Button b; Button b1; public EventTester3 (String title){ super(title); setLayout(new FlowLayout()); b=new Button("1"); b.addActionListener(new MyListener(1)); add(b); b1=new Button("not registred"); add(b1); setSize(100,100); setBackground(Color.blue); setVisible(true); } public static void main(String args[]){ EventTester3 f=new EventTester3 ("hello"); } } class MyListener implements ActionListener{ int count; public MyListener(int count){this.count=count;} public void actionPerformed(ActionEvent evt){ Button b=(Button)evt.getSource(); b.setLabel(new Integer(++count).toString()); } }
  • 255. 采用事件适配器public class EventTester4 extends Frame { Button b; public EventTester4 (String title){ super(title); setLayout(new FlowLayout()); b=new Button("1"); b.addMouseListener(new MyMouseListener(1)); add(b); setSize(100,100); setBackground(Color.blue); setVisible(true); } public static void main(String args[]){ EventTester4 f=new EventTester4 ("hello"); } }class MyMouseListener extends MouseAdapter{ int count; public MyMouseListener(int count){this.count=count;} public void mousePressed(MouseEvent evt){ Button b=(Button)evt.getSource(); //get event source b.setLabel(new Integer(++count).toString()); } }mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mouseClicked(MouseEvent)
  • 256. 一个组件注册多个监听者public class EventTester5 extends Frame { Button b; public EventTester5 (String title){ super(title); setLayout(new FlowLayout()); b=new Button("Mouse 1"); b.addMouseListener(new MyMouseListener1(1)); //register MyMouseListener b.addActionListener(new MyActionListener(1)); //register MyActionListener add(b); setSize(300,300); setBackground(Color.blue); setVisible(true); } public static void main(String args[]){ EventTester5 f=new EventTester5("hello"); } }
  • 257. 事件类class MyListener implements ActionListener{ int count; public MyListener(int count){this.count=count;} public void actionPerformed(ActionEvent evt){ Button b=(Button)evt.getSource(); b.setLabel(new Integer(++count).toString()); } }
  • 258. 事件监听接口
  • 259. 组件注册监听接口组件可以通过addXXXListener方法(XXX表示某种事件)注册监听者。 子类组件继承父类的所有注册监听者的方法。
  • 260. 事件处理练习: 为计算器加上事件处理,使它能进行简单的计算 ExGui4.java --> Calculater.java
  • 261. AWT绘图在Component类中提供了三个和绘图有关的方法: paint (Graphics g):绘制组件的外观。 update(Graphics g):调用paint()方法,刷新组件的外观。 repaint():调用update()方法,刷新组件的外观。 Graphics类提供了绘制各种图形的方法 drawLine(int x1,int y1,int x2, int y2) :画一条直线 drawString(String string,int left,int bottom) :写一个字符串 drawImage(Image image,int left, int top, ImageObserver observer) :画一个图片 drawRect(int left,int top,int width,int height):画一个矩形 drawOval(int x, int y, int width, int height) :画一个椭圆 fillRect(int left,int top,int width,int height):填充一个矩形 fillOval(int x, int y, int width, int height) //填充一个椭圆
  • 262. AWT绘图repaint()调用update(),update()调用paint()
  • 263. 一个绘图例子(SampleDrawer.java)按下[Change Color]按钮
  • 264. SampleDrawer.javapublic class SampleDrawer extends Frame implements ActionListener{ Color color=Color.red; Button b; public SampleDrawer(String title){ super(title); setLayout(new FlowLayout()); b=new Button("Change Color"); b.addActionListener(this); add(b); setSize(300,300); setVisible(true); } public void paint(Graphics g){ g.setColor(color); g.fillRect(100,100,100,100); g.setColor(Color.black); g.fillRect(0,100,100,100); g.fillRect(200,100,100,100); g.fillRect(0,200,300,100); } public static void main(String args[]){ SampleDrawer f=new SampleDrawer("hello"); } public void actionPerformed(ActionEvent evt){ if(color==Color.red) color=Color.green; else color=Color.red; repaint(); //call repaint() method } }
  • 265. 随机画椭圆OvalDrawer.javaOvalDrawer类的paint()方法负责画一个椭圆,OvalDrawer类还实现了Runnable接口,在run()方法中,每隔400毫秒就会随机的设置椭圆的起始坐标(x,y)、椭圆的宽width和高height,然后调用OvalDrawer的repaint()方法刷新界面。 public void run(){ while(true){ x=(int)(Math.random()*300); y=(int)(Math.random()*300); width=(int)(Math.random()*100); height=(int)(Math.random()*100); color=colors[(int)(Math.random()*(colors.length-1))]; repaint(); try{Thread.sleep(400); }catch(InterruptedException e){ throw new RuntimeException(e);} } }
  • 266. Swing组件在java.awt包中,提供了各种具体的组件,如窗体Frame、面板Panel、按钮Button、文本框TextField和文本区域TextArea等。AWT组件的优点是简单、稳定,兼容于任何一个JDK版本,缺点是依赖于本地操作系统的GUI,缺乏平台独立性。 为了使用Java创建的图形界面也能够跨平台,即在不同操作系统中保持相同的外观,从JDK1.2版本开始引入了Swing组件,这些Swing组件位于javax.swing包中,成为JDK基础类库的一部分。 Swing组件是用纯Java语言编写而成的,不依赖于本地操作系统的GUI,Swing组件可以跨平台运行。独立于本地平台的Swing组件被称为轻量级组件,而依赖于本地平台的AWT组件被称为重量级组件。
  • 267. JComponent多数Swing组件的父类为javax.swing.JComponent
  • 268. JFrameJFrame与Frame的最大区别在于前者不能直接通过add()方法加入组件,也不能直接通过setLayout()方法设置布局。 //以下代码非法 JFrame jFrame=new JFrame("Hello"); jFrame.setLayout(new GridLayout(2, 1)); jFrame.add(jLabel) ; jFrame.add(jButton);
  • 269. JFrame每个JFrame都有一个与之关联的内容面板(contentPane),只能针对这个contentPane设置布局,以及加入组件: JFrame jFrame=new JFrame("Hello"); //获得与JFrame关联的contentPane,contentPane默认的布局管理器为BorderLayout Container contentPane =jFrame.getContentPane(); contentPane.setLayout(new GridLayout(2, 1)); contentPane.add(jLabel) ; contentPane.add(jButton);
  • 270. JFrameJFrame的setDefaultCloseOperation(int operation)方法用来决定如何响应用户关闭窗体的操作,参数operation有以下可选值: JFrame.DO_NOTHING_ON_CLOSE:什么也不做。 JFrame.HIDE_ON_CLOSE :隐藏窗体,这是JFrame的默认选项。 JFrame.EXIT_ON_CLOSE :结束程序。frame.addWindowListener(new MyWindowListener()); class MyWindowListener extends WindowAdapter{ public void windowClosing(WindowEvent evt){ System.exit(0); } }
  • 271. SimpleSwingDemo.javaJButtonJLabel
  • 272. SimpleSwingDemo.javapublic class SimpleSwingDemo extends Jframe implements ActionListener{ private JLabel jLabel; private JButton jButton; private String labelPrefix ="Number of button clicks: "; private int numClicks = 0; public SimpleSwingDemo(String title){ super(title); jLabel = new JLabel(labelPrefix + "0"); jButton = new JButton("I am a Swing button!"); // 创建一个快捷键: 用户按下Alt-i键等价于点击该Button jButton.setMnemonic('i'); //设置鼠标移动到该Button时的提示信息 jButton.setToolTipText("Press me"); jButton.addActionListener(this); Container contentPane=getContentPane(); contentPane.setLayout(new GridLayout(2, 1)); contentPane.add(jLabel) ; contentPane.add(jButton); pack(); setVisible(true); //当用户选择JFrame的关闭图标,将结束程序 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { numClicks++; jLabel.setText(labelPrefix + numClicks); } public static void main(String[] args) { new SimpleSwingDemo("Hello"); } }
  • 273. 第11课 小应用程序参考《Java面向对象编程》的第18章
  • 274. 独立应用程序的概念所谓独立应用程序是指从一个Java类的main()方法开始运行的程序。在这种运行模式中,Java类的.class文件和Java虚拟机进程位于同一个台机器上
  • 275. 小应用程序的概念JApplet以及它的父类Applet与其他Java类的最大区别在于,它们可以作为小应用程序,在浏览器中运行。当浏览器访问Web服务器中的一个嵌入了Applet的网页时,这个Applet的.class文件会从Web服务器端下载到浏览器端,浏览器启动一个Java虚拟机来运行Applet。
  • 276. 主要的Applet方法 代表了Applet生命周期的主要活动内容的方法: init (),start(),stop(),destory() 代表了Applet访问环境资源的方法 getDocumentBase(), getCodeBase() getImage(URL base, String target),getParameter()
  • 277. Applet举例public class MyApplet extends Applet implements Runnable { String word; int fontSize; Thread changer; public void init() { word=getParameter("word"); fontSize=8; setSize(100,100); } public void start(){ changer=new Thread(this); changer.start(); } public void stop(){ changer.stop(); } public void paint(Graphics g) { g.setFont(new Font("newFont",Font.BOLD,fontSize)); g.drawString(word,30,80); } public void run(){ while(true){ fontSize+=4; repaint(); //The word will be redrawed with new fontSize try{ Thread.sleep(1000); }catch(Exception e){} if(fontSize>40) fontSize=8; } }//run() }
  • 278. HTML中的Applet标记以IE浏览器为例,IE浏览器支持JDK1.0版本中的Applet,只要在网页中加入一个标记就能嵌入一个Applet. 浏览器一遇到这个标记,就会下载相应的 Applet类文件,并启动Java虚拟机运行这个Applet。 hi.html
  • 279. 读取HTML文件中的参数 以下是Applet的init方法: init(){ String s1=getParameter(“none”); //s1=null String s2=getParameter(“word”); //s2=“hi” String s3=getParameter(“WORD”); //s3=“hi” System.out.println(s2==s3); //true String s4=getParameter(“longWord”); // s4=”hi all” }
  • 280. Applet的运行Applet通常运行于一个Web浏览器中 JDK附带有一个专为查看Applet而设计的工具appletviewer。它的运行命令为: Appletviewer [-debug] [-J] [-encoding ] url |File 例如: appletviewer hi.html
  • 281. 将Applet加载到独立应用程序中Applet是Panel的子类,可以把Applet放到Frame中,然后通过独立应用程序显式。 参见AppletShower.java FrameApplet
  • 282. JApplet与Applet的区别 不能直接向JApplet中加入组件,而必须首先 获得它的内容面板,然后向内容面板中加入组件, 内容面板默认的布局管理器为BorderLayout,这一 点和JFrame一样: Container contentPane=getContentPane(); contentPane.setBackground(Color.WHITE); contentPane.add(contrlButton, BorderLayout.NORTH);
  • 283. JApplet举例public class MyJApplet extends JApplet implements Runnable { String word; int fontSize; Thread changer; public void init() { word=getParameter("word"); fontSize=8; setSize(100,100); } public void start(){ changer=new Thread(this); changer.start(); } public void stop(){ changer.stop(); } public void paint(Graphics g) { super.paint(g); g.setFont(new Font("newFont",Font.BOLD,fontSize)); g.drawString(word,30,80); } public void run(){ while(true){ fontSize+=4; repaint(); //The word will be redrawed with new fontSize try{ Thread.sleep(1000); }catch(Exception e){} if(fontSize>40) fontSize=8; } }//run() }
  • 284. 在HTML中加入JAppletIE浏览器无法运行Java2中的JApplet,在这种情况下,可以把JApplet作为一种插件来运行。在IE浏览器中,插件的标记为,在Netscape浏览器中,插件的标记为。当浏览器遇到插件标记,就会启动相应的插件运行程序,通过该程序来运行特定的插件。 JApplet作为一种插件时,它的插件运行程序为JRE程序(Java Runtime Enviroment,Java运行时环境)。该程序的下载网址为:http://java.sun.com/j2se/1.5.0/download.jsp。JRE程序的名字为jre-1_5_0_06-windows-i586-p.exe。
  • 285. 在HTML中加入JApplet hi_1.html
  • 286. Socket编程与网络通信进程通过网络进行通信时,Java技术使用它的流模型。 Java中的socket也是建立在TCP/IP协议的基础上。 一个进程向另一个进程发出连接请求时,首先必须知道对方的地址:进程所在的IP地址或主机名,以及进程的端/口号 在Java编程语言中,TCP/IP socket连接是用java.net包中的类实现的
  • 287. Socket编程与网络通信
  • 288. 最小的TCP/IP服务器TCP/IP服务器应用程序采用了Java技术语言提供的网络类。ServerSocket类完成了建立一个服务器所需的大部分工作。 参见SimpleServer.java和ServerClient.java
  • 289. Socket通信练习1 创建一个服务器端,多个客户端。客户端每隔一秒向服务器端报告它的名字及当前时间。服务器端将客户端传送的内容在服务器端屏幕上打印出来。客户端可以运行在不同的机器上。服务器端可以同时连接的客户数目如果超过5,其他的客户请求必须等待。 参考: ComplexServer.java, ComplexClient.java java ComplexServer >output.txt java ComplexClient
  • 290. Socket通信练习2创建一个服务器端,多个客户端。客户端每隔一秒向服务器端报告它的名字及当前时间。服务器端将客户端传送的内容在服务器端屏幕上打印出来。客户端可以运行在不同的机器上。 服务器端首先创建一个服务线程池,每当接受到一个客户请求,从线程池中调度一个空闲状态线程和客户通信 参考: ThreadPool.java, SocketServer.java MyService.java , ComplexClient.java
  • 291. Socket通信练习3采用Http协议的客户程序,参见HttpClient.java DataOutputStream streamOut=new DataOutputStream(socketS.getOutputStream()); DataInputStream streamIn=new DataInputStream(socketS.getInputStream()); streamOut.writeBytes("GET "+m_URL+" HTTP/1.0 \r\n\r\n"); String s=new String(); boolean done=false; while(!done){ s=streamIn.readLine(); if(s==null)done=true; else { strHTML+=s+"\r\n";} }
  • 292. 反射Java Reflection Api的作用 检查运行时的对象 动态更新字段 调用方法 反射主要的类: java.lang.Class; java.lang.reflect.Method; java.lang.reflect.Field; java.lang.Constructor
  • 293. 检查运行时的对象1.通过对象的getClass方法,获得Class对象 2.通过Class对象的getMethods()方法获得所有的方法 3.通过Class对象的getFields()方法获得所有的public字段 Person person=new Person(); Class personClass=person.getClass(); Method[] personMethords=personClass.getMethods(); Field[] personFields=personClass.getFields();
  • 294. 动态访问对象的字段Field提供了 1.getXXX(Object)方法,获得特定对象的某个字段的值 2.setXXX(Object, XXX)来设置特定对象的个字段的值 以上XXX表示数据类型
  • 295. 动态访问对象的字段Person person=new Person("linda",10); Class personClass=person.getClass(); Field ageField=personClass.getField("age"); //设定person的年龄 ageField.set(person, new Integer(12)); //获得person的年龄 int age=ageField.getInt(person); System.out.print(age); 参看reflect.sample.AccessPersonFields类
  • 296. 动态访问对象的方法Method提供了 Object invoke(Object obj, Object[] args) 方法,调用某个特定对象的方法,如果参数或返回类型为基本类型,将自动转化为相应的包装类对象
  • 297. 动态访问对象的方法Person person=new Person("linda",10); Class personClass=person.getClass(); Class[] parameterClass=new Class[1]; parameterClass[0]=Class.forName("int"); Method setAgeMethod=personClass.getMethod("setAge",parameterClass); parameterClass=new Class[0]; Method getAgeMethod=personClass.getMethod("getAge",parameterClass); //设定person的年龄 Object params[]=new Object[1]; params[1]=new Integer(12); setAgeMethod.invoke(person, params); //获得person的年龄 Object ageObject=getAgeMethod.invoke(person, new Object[0]); int age=((Integer)ageObject).intValue(); System.out.print(age); 参看reflect.smple.AccessPersonMethods类
  • 298. 动态访问构造方法Constructor c = Person.class.getDeclaredConstructor( new Class[] { String.class,int.class }); Person p = (Person) c.newInstance( new Object[] { "linda",new Integer(10) }); System.out.println(p);参看reflect.smple.ConstructPerson类
  • 299. 动态代理动态代理是指客户通过代理类来调用其它对象的方法 动态代理使用场合: 调试 远程方法调用
  • 300. 动态代理客户 代理 对象接口代理接口
  • 301. 动态代理步奏1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法 public Object invoke(Object theProxy, Method method, Object[] params) throws Throwable{ try { Object retval = method.invoke(targetObj, params); // Print out the result System.out.println(retval); return retval; } catch (Exception exc){} } 参见DebugProxy.java
  • 302. 动态代理步奏2.创建被代理的类以及接口 参见TestTarget.java,TestInterface.javaTestInterfaceTestTarget Classimplements doTest1() doTest2()
  • 303. 动态代理步奏3.通过Proxy的静态方法 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个TestInterface代理 TestTarget target = new TestTarget(); // Create a proxy to wrap the original implementation DebugProxy proxy = new DebugProxy(target); // Get a reference to the proxy through the TestInterface interface TestInterface test = (TestInterface) Proxy.newProxyInstance( TestInterface.class.getClassLoader(), new Class[] { TestInterface.class }, proxy); 参见RunDebugTest.java
  • 304. 动态代理步奏4.通过TestInterface代理调用TestTarget的方法 System.out.println(test.doTest1("This is test ", 1)); test.doTest2(new String[] { "foo", "bar", "baz" }); 参见RunDebugTest.java