java 学习笔记(core java)部分

 
第一部分 Core Java 部分
第一章     Core Java 基础
第一节 Java 基础知识
java语言是解释执行,java源码是通过编译生成一种特殊的.class的中间字解码文件,然后再有JVM进行解释执行。
java语言对指针进行了上层的封装,它保证能够通过这个指针(引用),来访问有效的内存单元。java语言不允许多继承,使继承关系成树装图,每个类都只能由一个父类。java语言的开发效率高,但执行效率低(相当于c++的55%)。
java的垃圾回收机制,在java中new的对象不需要向c++一样进行delete操作,JVM会根据情况回收垃圾对象。(懒汉机制,等待资源没有的时候才回收)我们只能够建议JVM进行垃圾回收, 例如∶System.gc() ,RunTime.gc()这两个方法就是建议JVM进行垃圾回收的方法。
JDK,java开发工具包(类库和运行命令),JRE,java运行环境,JVM,java虚拟机(解释执行的核心,对字节码进行翻译成运行环境的机器码,它可以屏蔽平台差异。JVM是不跨平台的。)
JAVA_HOME,指明JDK安装的位置,CLASSPATH,指明类文件的位置,PATH,指明命令的可执行文件的位置。
java源文件的文件名必须和文件中定义public class的类名(大小写页要相同)相同。
java源代码中的main方法是程序的入口,下面是main方法的定义写法。
public static void main(String[] args){
              System.out.println("Hello world");
       }
java源文件也要先编译,使用javac xxx.java格式的命令得来编译,使用java xxx来运行。
定义包结构要放在有效代码的第一行,package xxx.xxx,包的定义在一个程序中只能由一个,在加上包定义之后编译可以使用javac -d 路径 xxxx.java,这个-d这个命令行的参数可以指定包结构的位置“.”代表当前目录。在运行时要使用类的全名
java xxx.xxx.xxxx用包名以点分隔。运行时要在包结构的上一层目录来运行。
※java 中的注释
单行注释 //......                多行注释 /* .......*/
文档注释/** ........<p>(换行标签)*/,用javadoc命令可以根据原码中的文档注释生成注释文档(html格式)。文档注释中可以使用html标签。
javadoc -d 路径 (指定注释文档的保存路径)
文档注释一般写在类定义之前,方法之前,属性之前。在文档注释中可以用 @author 表示程序的作者,@version 表示程序的版本,前两个注释符号要写在类定义之前,用于方法的注释@param 对参数进行注释,@return 对返回值进行注释 @throws对抛出异常的注释。
※jar 命令∶用于打一个xxx.jar文件
用法:jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...
选项:
    -c 创建新的存档
    -t 列出存档内容的列表
    -x 展开存档中的命名的(或所有的〕文件
    -u 更新已存在的存档
    -v 生成详细输出到标准输出上
    -f 指定存档文件名
    -m 包含来自标明文件的标明信息
    -0 只存储方式;未用ZIP压缩格式
    -M 不产生所有项的清单(manifest〕文件
    -i 为指定的jar文件产生索引信息
    -C 改变到指定的目录,并且包含下列文件:
如果一个文件名是一个目录,它将被递归处理。清单(manifest〕文件名和存档文件名都需要被指定,按'm' 和 'f'标志指定的相同顺序
示例1:将两个class文件存档到一个名为 'classes.jar' 的存档文件中:
       jar cvf classes.jar Foo.class Bar.class
示例2:用一个存在的清单(manifest)文件 'mymanifest' 将 foo/ 目录下的所有
           文件存档到一个名为 'classes.jar' 的存档文件中:
       jar cvfm classes.jar mymanifest -C foo/ .
一般在使用使用jar cvf 文件名.jar 文件所在路径(xxx/xxx/xxx.class)也可以压缩一个目录,只要在制定路径是指定为文件夹,jar命令的命令行参数在使用时可以以“-”开头,也可以不用。
java程序的运行过程,首先是启动java虚拟机,然后就是去找.class文件,先是从系统的类库中找(系统之会在跟目录下查找,所以需要完整类名),如果找不到的话会去CLASSPATH所设置的目录去找。然后加载到java虚拟机中。如果要使用到其他的在JAVA_HOME中没有的类或者是其他公司提供的第三方的.jar(jar包)文件时,要把它的路径及文件名加到CLASSPATH中。
系统会在每个java程序中隐含导入了java.lang这个包,import 包名,导入包中的类文件。
java.lang包,这是一个基础包。                     java.util包,这个包是工具类的包。
java.io包,这个包是用于输入输出操作的               java.net包,这个包是用于网络编程。
java.awt,java.swing,javax.swing java.event包,这些包用于java的图形编程用的包。
applaction java的应用程序,java应用程序中必须有一个main()方法。
第二节 标识符和关键字
Java代码中的“;”、“{}”、“ ”
Java语句以分号分隔,Java代码块包含在大括号内,忽略空格。
※标识符
1) 用以命名类、方法和变量、以及包遵守JAVA的命名规范类以每个单词。方法和变量第一个字母不大写,其他都以大写字母开头。2) 只能以字符、“_”或“$”开头; 3) 无长度限制。
※java 中的关键字
goto和const在java中虽然不再使用但是还作为关键字存在。
java中没有sizeof关键字了,java中的boolean类型的值只能用true和false,且这两值也是关键字。java语言中没有无符号(unsigned)关键字。
第三节 java 中的数据类型
   1) 整型
      byte       1字节     8位        -128到127
      short      2字节     16位       -2^15到2^15-1
      int        4字节      32位       -2^31到2^31-1
      long       8字节     64位       -2^63到2^63-1
   2) 浮点类型
      float      4字节      32位       
      double     8字节     64位
   3) 字符类型
      char       2字节     16位
   4) 布尔型
      boolean    false/true
注意:
1) char是无符号的16位整数,字面值必须用单引号括起来; ‘a’
 2) String 是类,非原始数据类型;
 3) 长整型数字有一个后缀为“L”或“l”,八进制前缀为“0”,十六进制前缀为“0x”;
 4) 黙认浮点类型为double;
 5) float数据类型有一个后缀为“f”或“F”,Double数据类型后可跟后缀“D”或“d”;
6)char类型也可以用通用转译字符,但是不能用ASCII码。可以用“/u0000 ”这种格式,因为char型中使用的是unicode编码方式。
注意:整型值存放,正数存放原码(二进制码),负数则存放补码(原码按位取反末位加一)。
注:实型值在存储时会损失精度,所以不要直接比较两个实型值。系统默认的实型都是double型,要使用float型时要在数据后加个f,或者强行转换,强转(占字节数大的类型转到占字节数小的类型)时会放弃高位值只取低位值。
java中的数字数据类型转换由占字节数小的类型到占字节数大的类型的可以有自动转换,反之则需要强行转换,char型和int型之间可以相互转换。char和short不能相互转换。
注意:隐式类型转换;
a 运算符 b ,如果a,b中有任意一个是double型,前面运算的结果就是double型,如果a,b中有任意一个是float型,前面运算的结果就是float型,如果a,b中有任意一个是long型,前面运算的结果就是long型,如果a,b中没有double、float、long型,那么其结果就为int型。
所有基本数据类型在使用时会事先分配空间,值本身就存在空间中,在传递时,就是值传递,不是引用传递。
       在类中定义的方法在返回值前加上static修饰符就可以在main方法中调用了。如果不用static那就需要在main方法中创建对象,使用对象来调用对象的方法。例如;
public class Test{
       public static void main(String[] args){
              Test t=new Test(); int b=1; int c=2;  int[] a=new int[10];
              t.sqort(a);     add(b,c)
       }
       public int[] sqort(int[] a){
              .......
       }
       static int add(b,c){
              .......
       }
}
第四节 java 中的运算符(java的运算符的优先级和结合性和c++相同)
System.out.println(3/2) 按整型计算 得1
1) >>= 前面是零补零,前面是一补一;
2) >>>= 无符号右移(强制右移都会移进一),
       >>=和>>>=对于负数不一样
   正数:右移n位等于除以2的n次方
   负数:变成正数。
3) &&    短路与,前面为假,表达式为假,后面的操作不会进行,& 会对所有条件进行判断。
4) ||    短路或,前面为真,表达式为真,后面的操作不会进行,| 会对所有条件进行判断。
   例:if(a<3&(b=a)==0)    b赋值
       if(a<3&&(b=a)==0) b不赋值
5)instanceof,是用于判断一个对象是否属于某个类型
6)java中的求余运算符“%”可以对两个实型变量求余
×? 注意:按位与是为了让某些位置一,按位或是令某些位置零,按位异或是令某些位取反。
注意:使用左右位移和无符号右移运算符的使用方法是 变量名<<=位移位数 ,变量名>>=位移位数 (前两个运算符是不会忽略整形符号位,也称逻辑位移),变量名>>>=位移位数
注意:左右位移和无符号右移运算符只能用于整形及其兼容类型(byte,int,short,long)
第五节 java 语句
※java 的流程控制
控制流
if()
if()….else
if()…..else if()….else
注意:else只是和其上面的同层的最近的if()来配对。
switch(){
case 'a':……..
case 1:……break;
default:
…………
}
注解:switch()内数据类型为byte short char int类型,只有以上四种类型的才可以在switch()中使用。case块中不加break时会按顺序执行下面的语句。
循环语句
for(int i=0;i<n;i++){}
while(){}
do{} while();-----------注意加分号
例子:
loop:for(int i=0;i<n;i++){
       for(int j=0;j<m;j++){
              if(3==j){
                     break loop;     /*loop为标签 只能用在循环语句中,循环
嵌套中用于跳到外层循环*/
}
       }
}
辨析:
       int x,a=6,b=7;
       x=a++ + b++;    // a=7,b=8,x=13
×?int x=6;x=~x;     // 6的二进制0110 取反得11001 再转成补码(取反加一) 10111 = -7
break,跳出本层循环,执行后面的代码,continue,提前终止本次循环,再一次进行循环或循环条件满足或不满足后退出循环。break 标签名; continue 标签名;这两条语句知识表示跳出有标签的循环和提前终止本次有标签的循环,只能用在循环语句(多层循环嵌套)中,循环嵌套中用于跳到外层循环。
注意:for循环在使用时一定要注意不要忘记()中的两个";",死循环的写法for(;;){}或者是用while(true){}
注意:System.out.println("..."+a)在使用这个语句时,它会将其中非字符串(String)的值转换成字符串(不是所有数据类型都可以的)。
第六节 数组
java中的数组Array,其包含两个部分,分别是数组的引用和数组的空间两部分。
声明数组
   1) 一组相同类型(可以是类)数据的集合;    2) 一个数组是一个对象;
   3) 声明一个数组没有创建一个对象;        4) 数组能以下列形式声明:
      int[] i 或 int i[]
      Car[] c 或 Car c[]
       *C++中只能 Car c[]
       *JAVA中推荐用 Car[] c;
    5)数组的定义 如:
      int[] a(数组引用声明)=new int[10](数组空间的声明,并把空间首地址赋值给数组的引用)
      int[] a;
      a=new int[20];
创建数组
   1) 创建基本数据类型数组     int[] i = new int[2];
   2) 创建引用数据类型数组 Car[] c = new Car[100];
   3) 数组创建后有初始值。
       数字类型为0 布尔类型为false 引用类型为null
注意:访问没有初始化的数组中的值,是会抛出异常的(NULLpointerException),java中只保证一位数组的地址是连续的,二维数组实际上是一维数组中有存储了一维数组的引用。
初始化数组
   1) 初始化、创建、和声明分开
      int[] i;
      i = new int[2];
      i[0] = 0;
      i[1] = 1;
   2) 初始化、创建、和声明在同一时间
      int[] i = {0,1};
      Car[] c = {new Car(),new Car()};
多维数组
   1) 有效定义
      int[][] i1 = new int[2][3]; (同时给定一维,二维的空间)
      int[][] i2 = new int[2][]; (给定一维的空间,二维空间待定)
      i2[0] = new int[2],i2[1] = new int[3];
          *C++中 int[][] =new int[][3];有效
   2) 无效定义
      int[][] i1 = new int[][3];
   3) 数组长度 ----- 数组的属性length(在二维数组中这个属性只代表第一维的长度)
      int[] i = new int[5];
      int len = i.length;          //len = 5;
      Student[][] st = new Student[4][6];
      len = st.length;           //len = 4;
      len = st[0].length;         //len = 6;
数组拷贝
System.arrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
src源数组,srcPos从第几位开始拷贝,dest目标数组,destPos目标数组放置的起始位置,length,表示要拷贝的长度。拷贝一个数组到另一个数组。
类的对象的创建和对象数组
一个xxx.Java文件中可以定义多个类但是只能由一个public修饰的类,也只能以这个类的类名作为.java的文件名。
java中的类的对象的创建,要先创建这个对象的引用, 例如:Car c;然后用new这个关键字创建一个对象的实例(对象的空间) 例如:c=new Car();,然后对象的实例的空间首地址赋值给对象的引用。多个对象的引用可以同时引用自同一个对象的实例,但是对象的引用只能引用一个对象的实例。
对象的引用和对象的实例间就像是牵着气球的线和气球一样。
注意:只有一个没有被任何对象的引用所引用的对象的实例才会变成垃圾等待被垃圾回收。
对象数组
例:Car[] c=new Car[3];
    c[0]=new Car();
注意:存放基本类型的数组的数据是直接存放在数组的空间中,而对象的数组在数组空间中存放的则是对象的引用。
定义在类中类的属性是实例变量,定义在类的方法中的变量是局部变量。实例变量是保存在对象空间中的,而局部变量则是在方法调用的分配空间,调用结束后就释放空间。
注意:在类的定义中属性的定义和方法的定义必须写在类里。
注意:系统会自动初始化实例变量,数字类型为0 ,布尔类型为false ,引用类型为null。局部变量需要初始化,必须赋初值。如果不赋初值无法通过编译。
Java中的方法调用中参数传递有两种,一个是对于参数是基本类型的使用的是值传递(直接传参数的值),另一个是引用传递,它是用于参数是类的对象,它传递的是这个对象的引用。
第二章 面向对象的编程
第一节   面向对象的思想
anything is Object(万物皆对象)
抽象,从对具体的对象中抽取有用信息。
对象有其固有属性,对象的方法,即对象的行为(对象能做什么)。
对象本身是简单的(功能简单),多个对象可以组成复杂的系统(对象之间彼此调用对方的方法)。
对象应当是各司其职(功能简单),各尽所能(把自己的功能作到最好)。(弱耦合性实现了前面所述的对象的特点)
对象的耦合性,是对象之间的联系,对象和系统之间的联系。对象的耦合性要尽量的弱,也就是对象之间的联系尽可能的弱,对象和系统之间的联系尽可能的弱。
系统的可插入性,是在系统中加入新的对象之后的系统稳定性。
对象的可替换性,是在系统中替换原有的对象之后的系统的稳定性。
复用性,即对象可否被重复使用,对象的功能越简单,复用性就越好。(对象的耦合性弱,复用性就比较强)
面向过程是先有算法,后又数据结构(怎么解决问题)
面向对象是先有对象(数据结构),后有算法。(用什么做)
类是某些有着相同属性的集合的抽象。
类是一个类对象的模板,对象是类的具体化。
类是一个新的数据类型,类的对象。
注意:局部变量的作用范围是在定义它的代码块以内,局部变量要先赋值后使用,在以一个重合的作用于范围内不允许两个局部变量命名冲突。局部变量局部优先,且与实例变量同名时会覆盖局部变量。
变量 包括简单变量(原始数据类型),对象变量。
方法的定义:
1,方法的修饰符(多个修饰符出现的顺序无关)           |
2,方法的返回值类型                                   |顺
3,方法名                                             |序
4,方法的参数表                                       |向
5,方法中允许抛出的异常                               |下
java中不能够在返回语句后写任何代码。JVM+解释器=JRE,JRE+类库=JDK
java中方法的重载(overload)方法名相同,参数表不同,返回值类型可以不同。调用时要给出明确参数并确定调用某一方法。在编译时,编译器会根据参数选择适当的方法,所以重载也叫编译时多态。
就近向上匹配原则
如果方法的参数表中的数据类型和调用时给出的参数类型不尽相同时会根据向上匹配的就近原则。(类型就近向上转化匹配)
注意:调用时要给出明确参数并确定调用某一方法,否则编译会出错。
对象使用者(调用其他对象的方法)对象(对象中的方法被调用时根据参数进行自己进行选择)
一类方法,但跟据不同的参数会有差异,对象会根据参数判断,对对象调用者透明。
创建对象的过程,1,分配空间 2,初始化属性 3,调用构造方法(有前提,不考虑继承关系)。
构造方法的写法,没有返回值类型,构造方法的方法命名必须和类名相同。如果在类中不写构造方法,系统会提供一个无参的构造方法。
注意:最好在写类时提供一个无参的构造方法。
获得对象的方式
通过new(在堆空间中申请分配空间),new 类名(),可以通过这种形式获得一个对象,这时的对象是无法使用,必须把的它的地址存放进一个对象变量才能够使用。例如 :Car c=new Car();
有参的构造方法在被调用时,在用new关键字或的对象时初始化,例如:Car c=new Car("yellow")
对象变量中存放的是对象的引用(地址的封装形式)
this关键字,表示当前对象(哪个对象调用了方法,哪个对象就是当前对象),可以用来区分实例变量和局部变量。this(),表示调用本类其他的构造方法, 注意,只能写在构造方法的第一行。
java中的参数传递,简单类型的变量传递的是数值,对象变量的传递则传递的一个引用(地址)。
第二节 封装、继承、多态
面向对象的三大特征:封装、继承、多态。
※java 中的封装
封装,一个对象和外界的联系应当通过一个统一的接口,应当公开的公开,应当隐藏的隐藏。(对象的属性应当隐藏),一个对象的内部是透明的,就是把对象内部的可透明性和隐藏的特性区分开,该透明的透明,该隐藏的隐藏。
(封装的属性)java中类的属性的访问权限的默认值不是private,要想隐藏该属性或方法,就可以加private(私有)修饰符,来限制只能够在类的内部进行访问。
对于类中的私有属性,要对其给出一对方法(getXxx(),setXxx())访问私有属性,保证对私有属性的操作的安全性。
方法的封装,对于方法的封装,该公开的公开,该隐藏的隐藏。方法公开的是方法的声明(定义),即(只须知道参数和返回值就可以调用该方法),隐藏方法的实现会使实现的改变对架构的影响最小化。。
封装会使方法实现的改变对架构的影响最小化。
完全的封装,类的属性全部私有化,并且提供一对方法来访问属性。
※java 中的继承
继承,是对有着共同特性的多类事物,进行再抽象成一个类。这个类就是多类事物的父类。父类的意义在于可以抽取多类事物的共性。
java中的继承要使用 extends关键字,并且java中只允许单继承,也就是一个类只能有一个父类。这样就使继承关系呈树状,体现了java的简单性。
子类只能继承在父类中可以访问的属性和方法(实际上父类中私有的属性和方法也会被继承但子类中无法访问罢了)。
※访问控制修饰符: (可以修饰属性和方法)
private修饰符,表示只有本类内部可以访问。
default修饰符,方法不加修饰符,会默认为default,表示在同一个包中可以访问,父子类在同一包中,子类可以继承父类的相应内容。(可以修饰类)
protected(保护)修饰符,表示同一包中可以访问,不同包的子类也可以访问继承。
public修饰符,表示公开,在任何地方都可以访问。(可以修饰类)
修饰符的权限是由上而下逐渐变宽的。
继承的意义,就在于子类可以在父类的基础之上对父类的功能进行发展,继承可以使系统的耦合性降低,也就是使对象间的联系便的松散,使多类对象间的联系用其父类对象代替。
注意:构造方法不能被继承。
父类的属性及方法的确定:要从子类的角度来看子类间的共性,当所有子类都有这个属性时,就应当考虑是否该放在父类中,方法也是如此,方法可以被看作是对象的行为,而类的方法这时这一类对象所共有的行为,所以也应当在方法的确定时注意是不是所有的子类型中都需要有这种方法,并且会根据不同的类型的行为的方式也不同才可以覆盖着个方法。
※java 中方法的覆盖
子类中有和父类中可访问(可继承到子类)的同名同返回类型同参数表的方法,就会覆盖从父类继承来的方法。
注意:在jdk1.4以前要求方法的覆盖时,需要方法的返回值,参数表,方法名必须严格相同,而在jdk1.5中方法覆盖,子类中的覆盖的方法的返回值可以是父类中被覆盖的方法的返回值类型的子类型。
注意:子类的方法覆盖父类的方法时,方法的修饰符要么相同,要么子类中的方法的修饰符表示的访问权限要宽于父类。父类中的私有方法,不能被继承到子类,就是说子类中即使将其覆盖了也不会有多态。
覆盖的意义:对从父类中继承的方法的发展。
注意:父子类中有同名的属性不叫子类覆盖了父类的属性,这种情况较作属性的遮盖(shadow)。
当构造有继承关系的对象的步骤
1,递归的构造父类的对象
2,分配空间
3,初始化本类实例变量(属性)
4,调用本类的构造方法
注意:子类对象中其实包含着父类的对象,也就是父类对象加上子类对象,才是完整的子类对象的实例。
super关键字
super(),表示在子类的构造方法中调用父类的构造方法(可以通过这种方法在子类的构造方法中初始化父类中的属性),super()也只能出现在构造方法的第一句上。super(),在子类的构造方中指明构造父类时调用哪一个父类的构造方法构造父类。
super,这里所表示的是一个父类的对象,可以通过super来使用父类中可以访问的方法(可以在父类中定义setXxx(),getXxx()方法来访问父类中的私有属性),super可以屏蔽父子类中同名属性的冲突。
注意:在写类的时候,一定要写默认无参的构造方法,如果一个构造方法的第一句既不是this(),也不是super()时,那么就会在这里隐含的调用它的父类的无参的构造方法,即隐含的有super()。
少覆盖原则:既子类应当尽量少的覆盖父类方法,如果覆盖了父类的大多数方法,那就应当考虑是否应当有继承关系
※java 中的多态(以子类覆盖了父类的方法为前提)
多态,把子类对象主观的看作是其父类型的对象,那么父类型就可以是很多种类型。
多态,编译时多态(方法的重载)
    运行时多态(多态)
编译时类型,也就是可以被看作的类型,主观认定。
运行时类型,也就是实际的对象实例的类型,客观不可改变(也是被看作类型的子类型)
对于一个对象来说,在对象产生时,运行时类型就已经确定不会再改变,编译时类型可以和运行时类型不同。在对象变量声明时可以确定其运行时类型,但是编译时类型对象变量背后所指向运行时类型则可以是其本类型或者是其子类型。
多态三特性
1,对象实例确定则不可改变(客观不可改变)。
2,只能调用编译时类型所定义的方法。
3,运行时会根据运行时类型去调用相应类型中定义的方法。
多态的意义:在需要使用一类对象的共性时,可以用多来屏蔽掉其子类中的差异。
注意:类的属性是没有多态的,只会根据编译时类型访问。只有子类覆盖了父类的方法,且把子类对象当作父类类型来看时才会有多态。要注意区分子类中的方法重载。对于方法的重载,则是会使用编译时类型来进行相应的方法调用。
两种复用
白箱复用,也就是继承复用,父类中的可以被子类访问到的就可以被继承,这样会有些不需要的内容被继承下来,所以这种方式不太好。
黑箱复用,也叫组合复用,也就是把要复用代码的类的对象作为本类中的一个属性,然后再通过方法的委托来实现由选择的复用,方法的委托就是在本类的方法内部通过该类的对象调用要使用类的方法。
注意:尽量用组合复用替代继承复用。
多态的使用
多态用于参数,可以在方法的参数中传入其父类类型,在运行时会根据实际的运行时类型来在方法中进行相应的操作。
多态用于返回值,可以在方法的返回值类型上是用其实际返回值的父类型,在使用期返回值时也不必关心其实际类型。
多态可以使代码变得更通用,以适应需求的变化。也就是定义在父类中的方法,可以在子类中有不同的实现将其覆盖,在为父类型的对象变量赋值相应需要功能的子类的对象实例。
第三节 java 中的修饰符
※static 表示静态,它可以修饰属性,方法和代码块。
1,static修饰属性(类变量),那么这个属性就可以用 类名.属性名 来访问,也就是使这个属性成为本类的类变量,为本类对象所共有。这个属性就是全类公有。(共有的类变量与对象无关,只和类有关)。
类加载的过程,类本身也是保存在文件中(字节码文件保存着类的信息)的,java会通过I/O流把类的文件(字节码文件)读入JVM(java虚拟机),这个过程成为类的加载。JVM(java虚拟机)会通过类路径(CLASSPATH)来找字节码文件。
类变量,会在加载时自动初始化,初始化规则和实例变量相同。
注意:类中的实例变量是在创建对象时被初始化的,被static修饰的属性,也就是类变量,是在类加载时被创建并进行初始化,类加载的过程是进行一次。也就是类变量只会被创建一次。
2,static修饰方法(静态方法),会使这个方法成为整个类所公有的方法,可以用类名.方法名 访问。
注意:static修饰的方法,不直接能访问(可以通过组合方式访问)本类中的非静态(static)成员(包括方法和属性),本类的非静态(static)方法可以访问本类的静态成员(包括方法和属性),可以调用静态方法。静态方法要慎重使用。在静态方法中不能出现this关键字。
注意:父类中是静态方法,子类中不能覆盖为非静态方法,在符合覆盖规则的前提下,在父子类中,父类中的静态方法可以被子类中的静态方法覆盖,但是没有多态。(在使用对象调用静态方法是其实是调用编译时类型的静态方法)
注意:父子类中,静态方法只能被静态方法覆盖,父子类中,非静态方法只能被非静态方法覆盖。
java中的main方法必须写成static的是因为,在类加载时无法创建对象,因为静态方法可以不通过对象调用
所以在类的main方法。所在在类加载时就可以通过main方法入口来运行程序。
注意:组合方式,就是需要在方法中创建一个所需要的对象,并用这个对象来调用任意所需的该对象的内容,不会再受只能访问静态的约束。
3,static修饰初始代码块,这时这个初始代码块就叫做静态初始代码块,这个代码块只在类加载时被执行一次。可以用静态初始代码块初始化一个类。
动态初始代码块,写在类体中的“{}”,这个代码块是在生成对象的初始化属性是运行。这种代码块叫动态初始代码块。
类在什么时候会被加载,构造(创建)对象时会加载类,调用类中静态方法或访问静态属性也是会加载这个静态方法真正所在的类。在构造子类对象时必会先加载父类,类加载会有延迟加载原则,只有在必须加载时才会加载。
※final 修饰符,可以修饰变量,方法,类
1,final修饰变量
被fianl修饰的变量就会变成常量(常量应当大写),一旦赋值不能改变,(可以在初始化时直接赋值,也可以在构造方法里也可以赋值,只能在这两种方法里二选一,不能不为常量赋值),fianl的常量不会有默认初始值,对于直接在初始化是赋值时final修饰符常和static修饰符一起使用。
2,final修饰方法,被final修饰的方法将不能被其子类覆盖,保持方法的稳定不能被覆盖。
3,final修饰类,被final修饰的类将不能被继承。final类中的方法也都是final的。
注意:final,不能用来修饰构造方法,在父类中如果有常量属性,在子类中使用常量属性时是不会进行父类的类加载。静态常量如果其值可以确定,就不会加载该类,如果不能确定则会加载该常量所在的类。
不变模式,对象一旦创建属性就不会改变。用final修饰属性,也用final修饰类(强不变模式),用final修饰属性(弱不变模式)。
不变模式的典型体现:java.lang.String类,不变模式可以实现对象的共享(可以用一个对象实例赋值给多个对象变量。)
池化的思想,把需要共享的数据放在池中(节省空间,共享数据)
只有String类可以用“”中的字面值创建对象。在String类中,以字面值创建时,会到Java方法空间的串池空间中去查找,如果有就返回串池中字符串的地址,并把这个地址付给对象变量。如果没有则会在串池里创建一个字符串对象,并返回其地址付购对象变量,当另一个以字面值创建对象时则会重复上述过程。
如果是new在堆空间中创建String类的对象,则不会有上述的过程。
String类中的intern()方法会将在堆空间中创建的String类对象中的字符串和串池中的比对,如果有相同的串就返回这个串的串池中的地址。
不变模式在对于对象进行修改,添加操作是使相当麻烦的,会产生很多的中间垃圾对象。创建和销毁的资源的开销是相当大的。
String类在字符串连接时会先的效率很低,就是因为它所产生的对象的属性是不能够修改的,当连接字符串时也就是只能创建新的对象。
对于很多的字符串连接,应当使用StringBuffer类,在使用这个类的对象来进行字符串连接时就不会有多余的中间对象生成,从而优化了效率。
※abstract (抽象)修饰符,可以修饰类和方法
1,abstract修饰类,会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型,也就是编译时类型,抽象类就像当于类的半成品,需要子类继承并覆盖其中的抽象方法。
2,abstract修饰方法,会使这个方法变成抽象方法,也就是只有声明(定义)而没有实现,实现部分以";"代替。需要子类继承实现(覆盖)。
注意:有抽象方法的类一定是抽象类。但是抽象类中不一定都是抽象方法,也可以全是具体方法。
abstract 修饰符在修饰类时必须放在类名前。
abstract修饰方法就是要求其子类覆盖(实现)这个方法。调用时可以以多态方式调用子类覆盖(实现)后的方法,也就是说抽象方法必须在其子类中实现,除非子类本身也是抽象类。
注意:父类是抽象类,其中有抽象方法,那么子类继承父类,并把父类中的所有抽象方法都实现(覆盖)了,子类才有创建对象的实例的能力,否则子类也必须是抽象类。抽象类中可以有构造方法,是子类在构造子类对象时需要调用的父类(抽象类)的构造方法。
final abstract private abstract static abstract ,这些是不能放在一起的修饰符,因为abstract修饰的方法是必须在其子类中实现(覆盖),才能以多态方式调用,以上修饰符在修饰方法时期子类都覆盖不了这个方法,final是不可以覆盖,private是不能够继承到子类,所以也就不能覆盖,static是可以覆盖的,但是在调用时会调用编译时类型的方法,因为调用的是父类的方法,而父类的方法又是抽象的方法,又不能够调用,所以上面的修饰符不能放在一起。
抽象(abstract)方法代表了某种标准,定义标准,定义功能,在子类中去实现功能(子类继承了父类并需要给出从父类继承的抽象方法的实现)。
方法一时间想不到怎么被实现,或有意要子类去实现而定义某种标准,这个方法可以被定义为抽象。(abstract)
※模板方法模式
用abstract把制订标准和实现标准分开,制定的标准就是模板,实现就是按模板标准来实现,也就是继承模板,实现模板中相应功能的方法。模板中不允许修改的方法可以用fianl来修饰,这个方法不能使抽象方法,为保证安全,封装,把模板中不公开的部分用protected(保护)修饰。
第四节 java 中的接口
接口是一种程序结构,是特殊的抽象类。接口中的方法必须都是公开的抽象方法(public abstract),接口中的属性都是公开静态常量(public static final)。
声明一个接口用 interface 关键字,接口也是一种类型,编译之后也有生成相应字节码,它的声明规范也要符合类型的定义(一个源文件中只能有一个public interface,接口名和源文件名相同,有public interface,就不能在写public class了)。接口中的属性可以不加修饰符,方法也不用加修饰符。
接口也可以继承,但是只能由接口继承,在用类去继承时要换用 implements 关键字,这时类和接口也不叫做继承关系,而是 实现关系,但其实质也是继承。
一个类可以继承也只能继承另外一个类,但是可以实现多个接口,其语法是在implements后面写接口名,多个接口以“,”分隔。
接口之间是可以多继承的,其语法和类的继承语法是相同的,在接口多继承时,在 extends后写接口名如果要继承多个接口,接口名以“,”分隔,接口的继承关系只是把其父接口中的抽象方法继承到子接口中。要实现接口就必须实现接口中的所有方法。
一个类可以在继承一个类的同时,也可以实现一个或多个接口。采用接口就绕开了单继承限制。
接口类型也可以做为编译时类型使用,但其实际的运行时类型必须是完全实现接口的类的对象实例,这样就使多态变得很灵活了,
注意:实现接口时,在实现(覆盖)抽象方法时,注意必须要在方法的返回值类型前加public修饰符。如果没有完全实现接口中的方法,那么这个类就只能够是个抽象类,不能创建对象。接口的是实质就是特殊的抽象类。接口没有构造方法。
接口的意义:
1,接口可以实现多继承。
2,用接口可以实现混合类型(主类型,副类型),java中可以通过接口分出主次类型。主类型使用继承,副类型,使用接口实现。
3,接口进一步深化了标准的思想,接口本身就是一个标准,它起到了降低耦合性的作用,接口可以使方法的定义和实现相分离,也就是将接口的定义者和实现者相分离,接口也可以用于降低模块间或系统间的耦合性。针对接口编程可以屏蔽不同实现间的差异,看到的只是实现好的功能,
接口:定义标准,
接口的实现:实现标准
接口的调用者:标准的使用
针对接口编程原则,也就是按照标准实现。
先有接口的定义,再有接口使用者,最后把接口的实现对象纳入接口的使用者中,接口的使用者会通过接口来调用接口实现者的方法。
接口的定义者定义好了标准,接口的使用者先写好了使用代码,接口的实现者写好实现之后把实现对象传入接口的使用者中。它调用接口中方法也就是调用接口实现中的方法。这种过程叫做接口的回调。
尽量使用接口类型作为编译时类型,尽量将抽取到的共性行为写在接口中。
用若干个小接口取代一个大接口。(接口隔离原则)
把一个类的功能作成接口,只暴露想暴露的方法,接口隔离原则可以实现更高层次的封装,针对的对象不同,暴露的方法也不同。
第五节  java 中的根类 Object
java中所有的类的父类或直接或间接的或隐含的都是Object类。
java不允许循环继承,也就是互相继承是不可以的。
Object类中的finalize()一个对象被垃圾收集的时候, 一个对象被垃圾收集的时候,最后会由JVM调用这个对象的finalize方法
Object类中有一个String toString()方法,返回该对象的字符串表示。Object类中的toString()方法他返回的是类名加上它的地址的一个字符串。在子类中推荐覆盖toString()方法。
Object类中的boolean equals(Object o)方法是用来比较对象的内容是否相等,其返回值是boolean类型的值,相同为真,不同则为假。实际上还是比较对象地址是否相同。String类覆盖了equals()方法,它比较是对象中的内容是否相同。子类中也推荐覆盖Object类中继承的equals()方法
※equals() 的覆盖原则,
自反性 x.equals(x) 为true
对称性 y.equals(x) 和 x.equals(y) 的值要相同,要么都为true,要么都为false。
传递性 x.equals(y)为true, y.equals(z)也为true ,那么x.equals(z)一定也为true。
※覆盖equals() 方法的步骤
boolean equals(Object o){
 if(this==o) return true;     //1,看看是不是一个对象
 if(o==null) return true;     //2,看看对象是不是空
 if(!(o instanceof 本类类名)) return false;//看看是不是本类对象
......//根据本类设计。
}
第六节 封装类
JAVA为每一个简单数据类型提供了一个封装类,使每个简单数据类型可以被Object来装载。除了int(Integer)和char(Character),其余类型首字母大写即成封装类类型名。
转换字符的方式:
int I=10;
String s=I+” ”;
String s1=String.valueOf(i);
Int I=10;
Interger I_class=new integer(I);
封装类.字符串.基本类型
Interger--------------------(Double(x.toString))------------>Double
String -----------------(Integer.valueOf() )---------------->Integer
Integer-----------------(x.toString() )--------------------->String
int----------------------(100+“”)------------------------->String
String------------------(Integer.parseInt() )--------------->int
Integer-----------------(Integer.intValue() )--------------->int
学会查看javadoc的帮助文档。要先关注要使用方法的返回值类型,也就是要获得内容的类型,然后看方法名,JDK中的方法名基本上是见名知义,参数表,就是看需要什么才可以获得的需要的那些内容,也要看自己能够提供什么。
注意:“==”在任何时候都是比较地址,这种比较永远不会被覆盖。
程序员自己编写的类和JDK类是一种合作关系。(因为多态的存在,可能存在我们调用JDK类的情况,也可能存在JDK自动调用我们的类的情况。)
注意:类型转换中double/interger/string之间的转换最多。
第七节 内部类
(注:所有使用内部类的地方都可以不用内部类,但使用内部类可以使程序更加的简洁,便于命名规范和划分层次结构)。
内部类是指在一个外部类的内部再定义一个类。
*内部类可为静态,可用PROTECTED和PRIVATE修饰。(而外部类不可以:顶级类只能使用PUBLIC和DEFAULT)。
*JAVA文件中没有public class 可以类名和文件不同名。
内部类:内部类也就是定义在类内部的类。
内部类的分类
成员内部类、局部内部类、静态内部类、匿名内部类(图形是要用到,必须掌握)。
※成员内部类
四个访问权限修饰符都可以修饰成员内部类。
内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。
内部类是一种编译时语法,在编译时生成的各自的字节码文件,内部类和外部类没有关系。
内部类中可以访问外部类的私有成员。
作为外部类的一个成员存在,与外部类的属性、方法并列。
内部类和外部类的实例变量可以共存。
在内部类中访问实例变量:this.属性
在内部类访问外部类的实例变量:外部类名.this.属性。
在外部类的外部访问内部类,使用out.inner.
※成员内部类的特点:
1.内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为private,但是对于处于其内部的内部类还是可见的。)
2.用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。
对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。
3.成员内部类不能含有静态成员。
建立内部类对象时应注意:
在外部类的内部可以直接使用inner s=new inner();(因为外部类知道inner是哪个类,所以可以生成对象。)
而在外部类的外部,要生成(new)一个内部类对象,需要首先建立一个外部类对象(外部类可用),然后在生成一个内部类对象。内部类的类名是外部类类名.内部类类名。
Outer o=new Outer();
Outer.Inner in=o.new.Inner()。
※静态内部类
(注意:前三种内部类与变量类似,所以可以对照参考变量)
静态内部类定义在类中,任何方法外,用static class定义。
静态内部类只能访问外部类的静态成员。
生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。
静态内部类的对象可以直接生成:
Outer.Inner in=new Outer.Inner();
而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用private来进行定义。
 
注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
 
例子:
对于两个类,拥有相同的方法:
class People
{
  run();
}
interface Machine{
   run();
}
此时有一个robot类:
class Robot extends People implement Machine.
此时run()不可直接实现。
interface Machine
{
       void run();
}
class Person
{
       void run(){System.out.println("run");}
}
class Robot extends Person
{
       private class MachineHeart implements Machine
       {
              public void run(){System.out.println("heart run");}
       }
       public void run(){System.out.println("Robot run");}
       Machine getMachine(){return new MachineHeart();}
}
class Test
{
       public static void main(String[] args)
       {
              Robot robot=new Robot();
              Machine m=robot.getMachine();
              m.run();
              robot.run();
       }
}
 
局部内部类
在方法中定义的内部类称为局部内部类。
与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。
 
注意:局部内部类不仅可以访问外部类私有实例变量,但可以访问外部类的局部常量(也就是局部变量必须为final的)
在类外不可直接访问局部内部类(保证局部内部类对外是不可见的)。
在方法中才能调用其局部内部类。
通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。
 
局部内部类写法
public class TestLocalInnerClass{
       public static void main(String[] args){
              Outer o=new Outer();
              final int a=9;
              o.print(a);
       }
}
 
class Outer{
       private int index=100;
       public void print(final int a){
              final int b=10;
              System.out.println(a);
              class Inner{
                     public void print(){
                            System.out.println(index);
                            System.out.println(a);
                            System.out.println(b);
                      }                        
              }
              Inner i=new Inner();
              i.print();
       }
}
 
匿名内部类
 
匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
 
匿名内部类的特点:
 
1,一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖。
2,只是为了获得一个对象实例,不许要知道其实际类型。
3,类名没有意义,也就是不需要使用到。
 
注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。
因其为局部内部类,那么局部内部类的所有限制都对其生效。
匿名内部类是唯一一种无构造方法类。
大部分匿名内部类是用于接口回调用的。
匿名内部类在编译的时候由系统自动起名Out$1.class。
如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。
因匿名内部类无构造方法,所以其使用范围非常的有限。
当需要多个对象时使用局部内部类,因此局部内部类的应用相对比较多。匿名内部类中不能定义构造方法。
 
匿名内部类的写法:
interface A{
       void ia();
}
class B{
       public A bc(){
              return new A{
                     void ia(){
                     }
              };
        }
}
使用匿名内部类:
B b=new B();
A a=b.bc();
a.ia();
 
Exception(例外/异常)
 
对于程序可能出现的错误应该做出预案。
例外是程序中所有出乎意料的结果。(关系到系统的健壮性)
 
JAVA会将所有的异常封装成为一个对象,其根本父类为Throwable。
 
异常的分类
 
Throwable有两个子类:Error和Exception。
 
一个Error对象表示一个程序错误,指的是底层的、低级的、不可恢复的严重错误。此时程序一定会退出,因为已经失去了运行所必须的物理环境。对于Error错误我们无法进行处理,因为我们是通过程序来应对错误,可是程序已经退出了。
 
我们可以处理的Throwable类中只有Exception类的对象(例外/异常)。
 
Exception有两个子类:Runtime exception(未检查异常)可以在编程时避免,可处理可不处理
                     非Runtime exception(已检查异常)必须进行处理。
 
注意:无论是未检查异常还是已检查异常在编译的时候都不会被发现,在编译的过程中检查的是程序的语法错误,而异常是一个运行时程序出错的概念。
 
在Exception中,所有的非未检查异常都是已检查异常,没有另外的异常!!
 
未检查异常是因为程序员没有进行必要的检查,因为他的疏忽和错误而引起的异常。一定是属于虚拟机内部的异常(比如空指针)。
 
应对未检查异常就是养成良好的检查习惯。
已检查异常是不可避免的,对于已检查异常必须实现定义好应对的方法。
已检查异常肯定跨越出了虚拟机的范围。(比如“未找到文件”)
 
异常的传递
 
如何处理已检查异常(对于所有的已检查异常都要进行处理):
首先了解异常形成的机制:
当一个方法中有一条语句出现了异常,它就会throw(抛出)一个例外对象(throw 异常对象),然后后面的语句不会执行返回上一级方法,其上一级方法接受到了例外对象之后,有可能对这个异常进行处理,也可能将这个异常转到它的上一级。
 
注意:当一个方法中出现了异常,没有进行异常处理,方法就会把异常对象作为返回值返回。如果有异常进入虚拟机,那么虚拟机就会立刻中止程序的执行。
 
异常的处理方式
 
非RuntimeException (已检查异常)这种异常必须处理。如果不处理编译出错。
 
对于接收到的已检查异常有两种处理方式:throws和try..catch(...){}方法。
 
注意:出错的方法有可能是JDK,也可能是程序员写的程序,无论谁写的,抛出一定用throw。
 
在方法的定义中声明方法可能抛出的异常,用(throws 异常类名,异常类名) ,声明这个方法将不处理异常,并把异常交给上一级方法处理。可以抛出的是实际产生异常的父类的异常对象。
 
例:public void print() throws Exception.
 
对于方法a,如果它定义了throws Exception。那么当它调用的方法b返回异常对象时,方法a并不处理,而将这个异常对象向上一级返回,如果所有的方法均不进行处理,返回到主方法,程序中止。(要避免所有的方法都返回的使用方法,因为这样出现一个很小的异常就会令程序中止)。
 
如果在方法的程序中有一行throw new Exception(),返回错误,那么其后的程序不执行。因为错误返回后,后面的程序肯定没有机会执行,那么JAVA认为以后的程序没有存在的必要。
 
try..catch捕获异常
 
对于try……catch格式:
try {可能出现错误的代码块}    catch(exception e){进行处理的代码} ;
                                对象变量的声明
 
用这种方法,如果代码正确,那么程序不经过catch语句直接向下运行;
如果代码不正确,则将返回的异常对象和e进行匹配,如果匹配成功,则处理其后面的异常处理代码。(如果用exception来声明e的话,因为exception为所有exception对象的父类,所有肯定匹配成功)。处理完代码后这个例外就完全处理完毕,程序会接着从出现异常的地方向下执行(是从出现异常的地方还是在catch后面呢?利用程序进行验证)。最后程序正常退出。
 
try块中的代码如果没有出现异常,就会跳过catch,正常执行。
try中如果发现错误,即跳出try块去匹配catch,那么try后面的语句就不会被执行。
一个try可以跟进多个catch语句,用于处理不同情况。当一个try只能匹配一个catch。
我们可以写多个catch语句,但是不能将父类型的exception的位置写在子类型的excepiton之前,因为这样父类型肯定先于子类型被匹配,所有子类型就成为废话,java中是不允许写废话的,所以编译会出错。
 
在try,catch后还可以再跟一子句finally。其中的代码语句无论如何(无论有没有异常)都会被执行(因为finally子句的这个特性,所以一般将释放资源,关闭连接的语句写在里面)。finally中的代码在和try中的代码的冲突时,finally中的代码一定会被执行且会忽略try中的代码。但是System.exit(0);(虚拟机退出语句)则不会去执行fianlly中的代码。
 
try{..}catch(..){..}
try{..}catch(..){}finally{..}
try{..}finally{}
以上三种写法都可以。
 
如果在程序中书写了检查(抛出)exception但是没有对这个可能出现的检查结果进行处理,那么程序就会报错。而如果只有处理情况(try)而没有相应的catch子句,则编译还是通不过。
 
如何知道在编写的程序中会出现例外呢
1.调用方法,查看API中查看方法中是否有已检查错误。
2.在编译的过程中看提示信息,然后加上相应的处理。
 
Throwable有一个message属性。在使用catch的时候可以调用:
Catch(IOException e){System.out.println(e.message())};
Catch(IOException e){e.printStackTrace()};
以上两条语句都是可以打印出错的过程信息。告诉我们出错类型所历经的过程,在调试的中非常有用。
 
开发中的两个道理:
①如何控制try的范围:根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,那么这个我们就说这两个程序代码存在关联,应该放在同一个try中。
②对已经查出来的例外,有throw(消极)和try catch(积极)两种处理方法。
对于throws把异常抛到try catch能够很好地处理例外的位置(即放在具备对例外进行处理的能力的位置)。如果没有处理能力就继续上抛。
 
当我们自己定义一个例外类的时候必须使其继承excepiton或者RuntimeException。
throw是一个语句,用来做抛出例外的功能。
而throws是表示如果下级方法中如果有例外抛出,那么本方法不做处理,继续向上抛出。
throws后跟的是例外类型。
 
注意:方法的覆盖中,如果子类的方法抛出的例外是父类方法抛出的例外的父类型,那么编译就会出错:子类无法覆盖父类。
结论:子类方法不可比父类方法抛出更多的例外。子类抛出的例外或者与父类抛出的例外一致,或者是父类抛出例外的子类型。或者子类型不抛出例外。如果父类型无throws时,子类型也不允许出现throws。此时只能使用try catch。
 
断言是一种调试工具(assert)
 
其后跟的是布尔类型的表达式,如果表达式结果为真不影响程序运行。如果为假系统出现低级错误,在屏幕上出现assert信息。
Assert只是用于调试。在产品编译完成后上线assert代码就被删除了。
 
java中的图形界面
 
GUI,图形化的用户接口,为了人机交互使用的。
 
构造图形界面的步骤
1,选择一个容器
2,设置容器的布局管理器
3,向容器添加组件
4,事件的监听
 
容器(Container)用于管理其他的组件的对象。组件必须放到容器里。
JFrame,这是一个最顶层的窗体容器,所有其他的组件必须放在顶层容器里。
JDialog 对话框容器,他要依附于其父组件,他不是一个顶层容器。
JPanel,他不是顶层容器,必须放在顶层容器中,任何一个容器都有add()方法,Panel面板是透明的(默认)。他也是一个组件。
 
布局管理:对于任何一个容器类中都有setLayout()方法,用容器对象调用这个方法,来设置容器的布局管理器(LayoutManager这是一个接口,所有布局管理器都实现了这个接口)。
 
可用的布局管理器:
FlowLayout,流式布局管。Panel的默认布局管理就是FlowLayout。
BorderLayout,按方位进行布局管理,(North,South,East,West,Middle)不明确指定,就会默认加载在中间(Middle),
add(Component comp,String place)这个方法是在指定的位置添加组件。
GridLayout,网格布局,通过行列,间距,来用网格分割,把组件放入如网格中,先行后列摆放组件。
CardLayout,卡片布局,面板重叠放置。
GridBogLayout,组件可以跨行跨列的网格布局。
 
注意:一定要在图形界面都其他功能都设置好之后才能设置可见性。
 
JButton :按钮
JTextField:单行文本域
JTextArea:多行文本区
JScrollPane:滚动窗体
JComboBox:下拉选择框
 
JRadioButton:单选按钮
JCheckBox:多选按钮
JList:多行列表
JLabel:标签
JPasswordField:密码输入框
JEditorPane:显示结构化文档
Border:边框
 
JMenuBar:菜单条
JMenu:菜单
JMenuItem:菜单项
JPopupMenu:弹出式菜单
 
JSlider:滑动条
JProgressBar:进度条
JTabbedPane:分层面板
JSplitPane:分隔面板
JToolBar:工具条
 
JFileChooser:文件选择器
JColorChooser:颜色选择器
 
显示对话框
JoptionPane 里面有很多静态方法可以弹出对话框
 
注意:具体的方法可以去参看Java2 SE的API文档。
awt事件模型(观察者模式)(重点)
 
事件模型中,包括事件源对象,事件处理者(事件监听者对象),事件对象。
 
 
事件源和事件处理者之间建立了授权关系,也就是在事件源类中有一个事件处理者的对象作为属性,也可能是一个事件处理者的集合。
      
          事件对象
事件源————————〉事件处理者
 
这就是事件模型的机制,也就是由事件源对象发送一个消息(事件对象),然后事件处理者调用相应的方法处理事件。
 
在事件监听器接口中定义的方法,都要以事件对象为参数。
 
一个事件源可以注册多个同类型的监听器,也可以注册多种多个事件监听器,一个事件监听器也可以为多个事件源服务。
 
首先了解一下什么是发消息:A,B,C三个类,分别作为事件源,事件处理者,事件对象。在A类中有一个B类的属性或者是一个内容为B类对象的集合,也就是事件源和事件处理者之间的建立了授权关系,在B类需要实现一个自定义的接口,这个自定义的接口继承了EventListener,EventListener接口中没有定义任何方法,这只是一个标记接口。实现在自定义接口中定义好的用于事件处理的方法,C类要继承EventObject类。这些方法是以事件对象为参数的b(C c),而后在A类a(C c)方法中使用B类的对象调用B类中的b(C c)方法,并把事件对象作为参数,并在main方法中用A类的对象调用了a(c)方法,这也就叫做A类对象给B类发送了消息。
 
也就是说事件源对象间接调用了事件监听器的方法,并以事件对象为实参传到事件监听器的方法中,要就叫事件源给事件监听器的方法发了一个消息(事件对象)。
 
例子如下:
import java.util.*;
//事件源类
class A{
       private String test;
       private List li=new ArrayList();
       public A(String test){
              this.test=test;
       }
       public String getTest(){return this.test;}
       public void addB(B b){
              this.li.add(b);
       }
       public void removeB(B b){
              this.li.remove(b);
       }
       public void fire(){
              C c=new C(this);
              Iterator it=li.iterator();
              while(it.hasNext()){
                     B b=(B)it.next();
                     b.b(c);
              }
       }    
}
//事件监听器的接口,要继承EventListener标记接口
interface Blistener extends EventListener{
       void b(C c);
}
//事件监听器,实现接口
class B implements Blistener{
       public void b(C c){
              A a=(A)c.getSource();
              System.out.println(a.getTest()+" "+c.getMessage());
       }
}
//事件对象类
class C extends EventObject{
       private String message;
       public C(Object src){
              super(src);
       }
       public void setMessage(String message){
              this.message=message;
       }
       public String getMessage(){return this.message;}      
}
public class Test{
       public static void main(String[] args){
              A a1=new A("Event");
              B b1=new B();
              c1.setMessage("Test");
              a1.addB(b1);//注册监听器
              a1.fire();//发送事件
       }
}
以上代码只是事例,在引入包之后可以运行。
 
事件对象继承自EventObject类,并可以通过getSource()方法获得事件源对象,当然需要在构造事件对象时将事件源对象传入,来区分是哪个事件源发出的事件,所以要用事件对象作为参数。
 
事件源,事件对象,监听接口,在java.awt包中提供了很多已经定义好的,只需要实现监听接口就好了。
 
在Java的图形编程中,所有动作(事件)都已经提供了相应的事件对象和事件监听接口,例如:实现窗口的关闭按钮,点击关闭按钮会发出相应的事件对象,相应的调用监听器中实现好的方法。相应的方法清参阅Java2 SE API帮助文档。
 
缺省适配模式,通过一个抽象类实现接口,抽象类中的接口方法实现,都是一个无意义的空实现,可以继承这个抽象类,只覆盖向覆盖的方法就可以了。
 
在java.awt.event包中,会有一些适配类,也就是把相应的XXXListener,换成XXXAdapter就是适配类。
在java.awt.event包中的ActionEvent类,在以下操作中会发送这个事件,
1,JButton组件,按钮被点击
2,JTextField组件,在单行文本域中按Enter键。
3,JCheckBox组件,选中了复选框。
4,JRadioButton组件,选中了单选按钮。
5,JMenu组件,选中菜单项。
标准I/O流与文件
 
 _______    输入      __________
|        |————> |          |
| JVM |           | 数据资源 |
|_______| <————|__________|
            输出
 
对文件的操作
     
File类(java.io.File)可表示文件或者目录(在JAVA中文件和目录都属于这个类中,而且区分不是非常的明显)。
 
File下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。
 
注意:File类的对象实施表示一个文件并不是真正的文件,只是一个代理而已,通过这个代理来操作文件创建一个文件对象和创建一个文件在JAVA中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真正地创建到OS的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件才是在系统中真正地建立一个文件。
例如:File f=new File(“11.txt”);//创建一个名为11.txt的文件对象
f.CreateNewFile();      //真正地创建文件
 
File类的方法
 
Boolean createNewFile() //创建文件
Boolean mkdir() //创建目录
Boolean mkdirs() //创建多个目录
Boolean delete() //删除文件,删除的是创建File对象时指定与之关联创建的那个文件。
Boolean deleteOnExit(); //在JVM进程退出的时候删除文件,这样的操作通常用在临时文件的删除。
String[] List()://返回当前File对象下所以显文件和目录名(相对路径)
File[] ListFiles()://返回当前File对象(必须是目录)所有Files对象,可以用getName()来访问到文件名。
isDirectory()和isFile()//来判断究竟是目录还是文件。
String getParent()//得到父类文件名,只有在构造对象时传入了Parent对象才有。
File getParentFile()//父路径名的抽象路径名,如果没有指定Parent对象,则返回 null。   
String getPath()//获得相对路径。
exists() 判断文件或文件夹是否存在。
getAbsolutePath() //获得文件的绝对路径
 
处理跨平台性
 
对于命令:File f2=new file(“d://abc//789//1.txt”)
这个命令不具备跨平台性,因为不同的OS的文件系统的分隔符是不相同。
使用file类的separtor属性,返回当前平台文件分隔符。
File newD = new File("aa"+File.separator+"bb"+File.separator+"cc");
       File newF = new File(newD,"mudi.txt");
       try{
       newD.mkdirs();
       newF.createNewFile();
       }catch(Exception e){}
 
Java中的I/O
 
使用I/O流访问file中的内容。
JVM与外界通过数据通道进行数据交换。
分类:按流分为输入流和输出流,按传输单位分为字节流和字符流,还可以分为节点流和过滤流。
 
节点流:负责数据源和程序之间建立连接。
过滤流:用于给节点增加功能。在关闭流时如果使用了过滤流,只需关闭最外层的流就可以了。
 
过滤流的构造方式是以其他流为参数构造(这样的设计模式称为装饰模式)并且过滤流本身不能使用,他的作用只是增强节点流的功能。
 
字节输入流:
 
InputStream类 (抽象类)
 
io包中的InputStream为所有字节输入流的父类。
Int read();读入一个字节(每次一个);
可先使用new byte[]=数组,调用read(byte[] b),byte[]数组长度决定了可以读取到的最大字节数,用来调整效率。read (byte[])返回值可以表示有效数;read (byte[])返回值为-1表示结束。
 
字节输出流:io包中的OutputStream位所有字节输入流的父类。Write和输入流中的read相对应。
 
在流中close()方法由程序员控制。因为输入输出流已经超越了VM的边界,所以有时可能无法回收资源。
原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
以Stream结尾的类都是字节流。
如果构造FileOutputStream的同时磁盘会建立一个文件。如果创建的文件与磁盘上已有的文件名重名,就会发生覆盖。
用FileOutputStream中的boolean,则视添加情况,将数据覆盖重名文件还是将输入内容放在文件的后面。(编写程序验证)
 
 
所有字节输入流的父类,
如:FileInputStream,ObjectInputStream,PipedInputStrean都是InputStream类的子类。
 
1) 三个基本的read()方法
      a. int read(): 从流里读出的一个字节或者-1; (实际读了多长)
      b. int read(byte[]):将数据读入到字节数组中,并返回所读的字节数; (期望读了多长)
      c. int read(byte[], int , int):两个int参数指定了所要填入的数组的子范围。
2) 其它方法
      a. void close(): 关闭流,如使用过滤器流,关闭最外部的流,会关闭其余的流。
      b. int available(): 返回可从流中读取的字节数。
      c. skip(long): 丢弃了流中指定数目的字符。
      d. boolean markSupported()
      e. void mark(int)
      f. void rese()
 
OutputStream类(抽象类)
所有输出流的父类。
 
1) 三个基本的write()方法
      a. void write():
      b. void write(byte[]):
      c. void write(byte[], int , int)
2) 其它方法
      a. void close(): 关闭流,如使用过滤器流,关闭最外部的流,会关闭其余的流。
      b. void flush(): 允许你强制执行写操作。
注意:在流中close()方法由程序员控制。因为输入输出流已经超越了JVM的边界,所以有时可能无法回收资源。
原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
 
基本输入输出所使用的类的介绍:
 
FileInputStream和FileOutputStream (文件输入输出流)
 
以上两个是字节流
1) 结点流,可以对磁盘文件进行操作。
2) 要构造一个FileInputStream, 所关联的文件必须存在而且是可读的。
3) 要构造一个FileOutputStream而输出文件已经存在,则它将被覆盖。
      
       FileInputStream infile = new FileInputStream("myfile.dat");
    FIleOutputStream outfile = new FileOutputStream("results.dat");
       FileOutputStream outfile = new FileOutputStream(“results.dat”,true);
       参数为true时输出为添加,为false时为覆盖。
 
       FileOutputStream类代码:(为什么能建文件)
              Public FileOutputStream(String name){
                     This(name!=null?new File(String):null,false);
              }
 
 
DataInputStream和DataOutputStream(字节流)
 
以上两个是过滤流。通过流来读写Java基本类,注意DataInputStream和DataOutputStream的方法是成对的。 支持直接输出输入各种数据类型。    
writeBoolean(boolean b),readBoolean() ------以1bit数据传送
writeByte(int),readInt()------以1 byte数据传送
writeBytes(String s),readLine() (已过时方法)将字符串按字节顺序写出到基础输出流中以byte序列数据传送
writeChar(int v),readChar()――――――以 2 byte
writeChars(String s),readChar()-------------以 2 byte序列
writeDouble(double d),readDouble() -------以 8 byte
writeInt(int v),readInt()
writeLong(long l),readLong()
writeShort(short s),readShort()
writeUTF(String),readUTF()-----------能输出中文!
 
注意:使用DataOutputStream/DataInputStream时,要注意写入顺序和读取顺序相同,否则会将没有分割写入的信息分割不正确而读取出错误的数据。
 
 
 
BufferInputStream和BufferOutputStream(带缓存的字节流)
 
以上两个是过滤流,可以提高I/O操作的效率 ,用于给节点流增加一个缓冲的功能。
 
在JVM的内部建立一个缓冲区,数据先写入缓冲区,等到缓冲区的数据满了之后再一次性写出,效率很高。使用带缓冲区的输入输出流的速度会大幅提高,缓冲区越大,效率越高。(这是典型的牺牲空间换时间)
 
切记:使用带缓冲区的流,如果数据数据输入完毕,使用flush()方法将缓冲区中的内容一次性写入到外部数据源。用close()也可以达到相同的效果,因为每次close()都会调用flush()。一定要注意关闭外部的过滤流。
 
管道流(和UnixC++中的FIFO相同)
 
PipedInputStream和PipedOutputStream (字节流)
 
这两个是节点流,注意,用来在线程间通信。
 
               PipedOutputStream pos=new PipedOutputStream();
              PipedInputStream pis=new PipedInputStream();
              try
              {
                     pos.connect(pis);
                     new Producer(pos).start();//线程类对象,在构造时,使用管道流通信
                     new Consumer(pis).start();//线程类对象,在构造时,使用管道流通信
              }
              catch(Exception e)
              {
                     e.printStackTrace();
              }
 
 
随机存取文件
 
RondomAccessFile类允许随机访问文件,这个类也是支持直接输出输入各种数据类型。
GetFilepoint()可以知道文件中的指针位置,使用seek()定位。
Mode(“r”:随机读;”w”:随机写;”rw”:随机读写)
1) 实现了二个接口:DataInput和DataOutput;
2) 只要文件能打开就能读写;
3) 通过文件指针能读写文件指定位置;
4) 可以访问在DataInputStream和DataOutputStream中所有的read()和write()操作;
5) 在文件中移动方法:
a. long getFilePointer(): 返回文件指针的当前位置。
b. void seek(long pos): 设置文件指针到给定的绝对位置。
c. long length(): 返回文件的长度。
 
 
字符流
 
字符流可以解决编程中字符的编码问题。从字符到整数,对字符集和整数集建立一一对应的关系,就算叫做编码,从整数映射到字符,就叫做解码。
 
编码问题:
 
字节流的字符编码:
字符编码把字符转换成数字存储到计算机中,按ASCii将字母映射为整数。
把数字从计算机转换成相应的字符的过程称为解码。
 
编码的方式:
每个字符对应一个整数。不同的国家有不同的编码,当编码方式和解码方式不统一时,产生乱码。因为美国最早发展软件,所以每种的编码都向上兼容ASCII 所以英文没有乱码。
 
ASCII(数字、英文)1个字符占一个字节(所有的编码集都兼容ASCII)
ISO8859-1(欧洲) 1个字符占一个字节
GB-2312/GBK         1个字符占两个字节
Unicode             1个字符占两个字节(网络传输速度慢)
UTF-8               变长字节,对于英文一个字节,对于汉字两个或三个字节。
 
 
InputStreamReader和OutputStreamWriter(字节流转化成字符流的桥转换器)
这两个类不是用于直接输入输出的,他是将字节流转换成字符流的桥转换器,并可以指定编解码方式。
 
Reader和Writer (字符流类,所有字符流的父类型)
 
1) Java技术使用Unicode来表示字符串和字符,而且提供16位版本的流,以便用类似的方法处理字符。
2) InputStreamReader和OutputStreamWriter作为字节流与字符流中的接口。
3) 如果构造了一个连接到流的Reader和Writer,转换规则会在使用缺省平台所定义的字节编码和Unicode之间切换。
 
BufferedReader/(BufferedWriter,不常用)(这两个类需要桥转换)
PrintWriter(带缓存的字符输出流,不需要桥转换)
 
常用输入输出类型,不需要桥接,其中其它方法请参看API文档。
以上两个都是过滤流,需要用其他的节点流来作参数构造对象。
BufferedReader的方法:readLine():String ,当他的返回值是null时,就表示读取完毕了。要注意,再写入时要注意写换行符,否则会出现阻塞。
BufferedWriter的方法:newLine() ,这个方法会写出一个换行符。
PrintWriter的方法:println(….String,Object等等)和write(),println(...)这个方法就不必再写换行符了,在使用时会自动换行。
 
注意:在使用带有缓冲区的流时,在输入之后就要flush()方法,把缓冲区数据发出去。
 
原则:保证编解码方式的统一,才能不至于出现错误。
java.io包的InputStreamread输入流的从字节流到字符流的桥转换类。这个类可以设定字符转换方式。
OutputStreamred:输出流的字节流桥转换成字符流
Bufferread有readline()使得字符输入更加方便。
在I/O流中,所有输入方法都是阻塞方法。
Bufferwrite给输出字符加缓冲,因为它的方法很少,所以使用父类PrintWrite,它可以使用字节流对象,而且方法很多。
ObjectInputStream和ObjectOutputStream(对象流)
 
对象流是过滤流,需要节点流作参数来构造对象。用于直接把对象写入文件和从文件读取对象。只有实现了Serializable接口的类型的对象才可以被读写,Serializable接口是个标记接口,其中没有定义方法。对象会序列化成一个二进制代码。
 
writeObject(o),readObject()这两个是对象读写操作时用的方法。
 
       Object o = new Object();     
       FileOutputStream fos=new FileOutputStream("Object.txt");
       ObjectOutputStream oos=new ObjectOutputStream(fos);
       oos.writeObject(o);
       oos.close();
 
       FileInputStream fis =new FileInputStream(“Object.txt”);
       ObjectInputStream ois =new ObjectInputStream(fis);
       Object o = (Object)Ois.readObject();
       ois.close();
 
对象流读取结束返回 EOFException异常对象。
 
一个类中有其他类型的对象,那么,这个类实现了Serializable接口,在对象序列化时,也同样要求这个类中属性都能够对象序列化(基本类型除外)。
 
注意:对于对象流的操作,在写对象时要一次写入完毕,如果使用追加模式写入,只会读取到上一次写入的对象,使用对象流写入时,会先写入一个头部,然后写入数据,最后加上结束符号,如果使用追加方式写入的话,那就会在结束符号继续向下写入,但是在读取时只会读到结束符为止,以后再次写入的数据就会丢失。
 
注意:在使用对象流写入对象时要一次向文件写入,不能够采用追加方式。
 
serialver命令判断是否一个属性或对象可序列化,
serialver TestObject(TestObject必须为已经编译,也就是.class)
执行结果:如果不可序列化;则出现不可序列化的提示。如果可以序列化,那么就会出现序列化的ID:UID。
 
Externalizable这是Serializable的子接口,他可以让用户自定义如何序列化对象。
readExternal(ObjectInput in),writeExternal(ObjectOutput out)这是这个接口中的两个方法,通过这两个方法可以定制序列化过程。这个方法不安全,可以调用以上两个方法改变对象的状态。
 
transient只能用来修饰属性。表示这个属性在对象序列化时将被忽略。
transient int num;
表示当我们对属性序列化时忽略这个属性(即忽略不使之持久化)。所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。
 
 
java.util.StringTokenizer类,这个类是用于字符串截取的。
StringTokenizer(参数1,参数2)按某种符号隔开文件
StringTokenizer(s,”:”) 用“:”隔开字符,s为对象。
 
补充:字节流结束返回-1,字符流结束返回null,对象流结束返回 EOFException
引申---------〉异常经常被用在流程控制, 异常也是方法的一种返回形式。
第三章   集合、多线程以及 Java 5.0 新特性
集合类
 
集合(集合类的对象)是用来管理其他若干对象的,它类似于C++标准模板库中的容器,不过在JAVA的集合类的对象中可以用来存放多种类型的对象。
 
接口和类共同构成了一个集合框架,集合的概念,一个对象可以装载多个对象,这个对象就是集合对象。
 
集合框架
 
1,接口
 
Collection 用来管理多个对象,集合中的每个元素都是对象。
 
Map,Map中没有对象,而是键值对,由Key,value组成的键值对,Key是不可重复的。value是可以相同的,一个Key和一个value一一对应。
 
集合中用到的类,接口在java.util包中,在使用时注意将其引入import。
 
Collection 接口(以下介绍其子接口)
 
1)List 一个List的实现类的对象在管理多个对象时会按顺序组织对象(即按照将对象放入的顺序存储),List实现类的对象是有顺序的,List实现类对象中的内容是是可重复的。(注意,顺序和排序的区别)
 
2)Set 一个Set的实现类表示一个数学概念上的集合,Set的实现类的对象中的元素是无顺序的,也就是不会按照输入顺序来存放,Set的实现类对象中的元素是不重复的。
 
3)SortedSet,他是Set的子接口,他的实现类会对集合中的元素进行排序。但是要指定排序规则,他会按排序规则进行排序。
 
Map 接口(以下介绍其子接口)
 
SortedMap,这个接口的实现类同样可以实现,不过是对键值对中的Key进行排序,这个接口的实现类也是要指定排序规则的。
 
JDK1.4中的集合是不安全的对象,JDK5.0中解决了这个问题。
 
List接口的实现类
 
1> ArrayList是接近于功能的集合类,ArryList的实质就是一个会自动增长的数组,ArrayList是用封装的数组来实现的List接口的。
 
Collection的实现类对象的遍历方式是用迭代来实现的。
在使用迭代器时先要活得一个迭代器的对象,Iterator(迭代器接口)这是一个接口,迭代器是在集合类中实现的,也就是说,他是一个内部类(匿名内部类)实现的。
Iterator接口中定义的常用方法方法hasNext(),next()。
hasNext(),这个方法会使用一个游标,并通过判断游标指向的位置是否存放有对象。
next()方法也是Iterator接口中定义好的方法,这个方法会使游标指向下一个元素的位置,游标会跳过第一个元素,并返回其中的内容。
 
Collections 这是一个工具类,也是java.util包中的,这个类中的sort(list接口的实现类的对象)方法,其参数是一个集合类的对象,这个方法使用来对集合类的对象进行排序的。以后,我将以集合这个名字来称呼集合类的对象。,对于字符串对象内容的集合来说会按字典顺序排序(升序),对于数字内容的集合排序也会按照升序排序。
 
排序可一份为两部分内容,一个是排序的规则,也就是按照什么来进行排序,并且排成什么样的顺序。
第二个就是排序的算法,他决定了排序的效率。
 
在对自定义的集合内容类型排序时,需要先定义那个类型的排序规则。
Comparable接口,这个接口中只定义了一个compareTo(Object o),方法的返回至类型是整型,如果当前对象大于参数对象就返回正数,当前对象等于参数对象是就返回0,当前对象小于参数对象时就返回负值,这样写就是升序排列,反之则是进行降序排列,在实现这个接口中的方法时,返回值定义方式,只有这两种。
 
根据指定类型的排序规则实现了Comparable接口,那么就可以对存有这个类型的集合进行整体排序。Comparable接口,也叫做可比较接口。这个接口在java.lang包下。只要实现了这个接口,就是可排序的。
 
接下来介绍另外一种对自定义类型对象的集合整体排序的方法,也就是实现比较器接口(Comparator),这个接口中定义了一个compare(Object o1,Object o2)方法来比较两个对象,这个方法的返回值定义和上面介绍的那个方法是一样。
 
注意:在API,帮助文档中以上两个方法的参数类型是T,这代表的模板类型,也就是集合中存放的内容的类型,在JDK1.4中其参数就是Object类型,模板类型的详细内容会在最后的JDK5.0新特性中讲到。
 
Comparator接口可以在匿名内部类中实现,Collections 中的sort(集合了的对象,比较器)方法,可以对自定义类型内容的集合进行整体排序。
 
2> LinkedList,它是List接口的实现类,其底层是用双向循环链表来实现的。
 
注意:ArrayList的查询效率比较高,增删动作的效率比较差,适用于查询比较频繁,增删动作较少的元素管理的集合。
      LinkedList的查询效率低,但是增删效率很高。适用于增删动作的比较频繁,查询次数较少的元素管理集合。
 
ArrayList,LinkedList都是线程不安全的。
 
实现堆栈 1,数组(ArrayList,增删效率比较低,不适合)
        2,LinkedList(实现堆栈的好方法)
        3,java.util.Stack类,Stack是Vector的子类,Vector类是一个线程安全的(是一个重量级的类),并继承了Vector的方法,Verctor类(这个类也是List接口的实现类)和ArrayList的功能近乎相同。(不推荐使用Stack类来实现堆栈)。
 
Set接口的实现类
 
HashSet
 
Set的实现类的集合对象中不能够有重复元素,HashSet也一样他是使用了一种标识来确定元素的不重复,HashSet用一种算法来保证HashSet中的元素是不重复的,HashSet的底层实现还是数组。
 
Object类中的hashCode()的方法是所有子类都会继承这个方法,这个方法会用Hash算法算出一个Hash(哈希)码值返回,HashSet会用Hash码值去和数组长度取模,模(这个模就是对象要存放在数组中的位置)相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会添加进去。
 
Hash算法是一种散列算法。
 
注意:所以要存入HashSet的集合对象中的自定义类必须覆盖hashCode(),equals()两个方法,才能保证集合中元素容不重复。在覆盖和hashCode()方法时,要使相同对象的hashCode()方法返回相同值,覆盖equals()方法再判断其内容。为了保证效率,所以在覆盖hashCode()方法时,也要尽量使不同对象尽量返回不同的Hash码值。
 
如果数组中的元素和要加入的对象的hashCode()返回了相同的Hash值(相同对象),才会用equals()方法来判断两个对象的内容是否相同。
 
SortedSet接口是Set的子接口。
TreeSet是SortedSet接口的实现类,他可以对集合中的元素进行排序。
要存放在TreeSet中自定义类的对象,这个类要么是已经实现了Comparable接口,要么是能给出Comparator比较器,TreeSet可以自动过滤掉重复元素所以不用重载hashCode()方法,TreeSet会根据比较规则判断元素内容是否相同,TreeSet会在元素存入世就进行了排序。(在TreeSet给出排序规则时,一定要注意对象内容相等的条件,一定要注意在主观的认为两个对象内容相同时,才可以使用比较少的条件来进行判断)
 
在要排序时才使用TreeSet类(存储效率比较低),HashSet的存储效率比较高,在需要为HashSet的对象排序时,就可以把HashSet中的元素放入TreeSet。
 
 
Map接口的实现类
 
Map中只可以存放键值对(Key,value),其中Key是不可以重复的。Key和value是一一对应的。
 
HashMap,是Map接口的实现类,Key时无序存放的,其中Key是不可以重复的,它也是通过Hash码值来保证Key不重复的,Key和value是一一对应的。如果要加入的键值对和HashMap中键值对的Key是相同的就会将这个集合中的Key所队应的value值进行覆盖,在使用自定义类型作为Key时,那就是要覆盖hashCode(),equals()方法,也就是和HashSet中要放入自定义类型是的处理方法相同。这个类的对象是线程不安全的。
 
在遍历Map时,要使用其keySet()方法获得Key的一个Set集合,可以通过遍历这个Set,用get()方法来获得Key所对应的value,也就遍历了Map。
 
Hashtabl,也是Map接口的实现类,他和HashMap比较相似,只不过这个类对象是重量级的,也是线程安全的。他不允许Key和value为null。
Properties,这个类是Hashtable的子类,他的Key和value只能是字符串。
SortedMap是Map的子接口
TreeMap,是SortedMap的实现类,他会按照Key进行排序。和TreeSet类一样,在使用自定义类作Key时,要用自定义类实现Comparable接口。
注意:其实HashSet底层就是一个HashMap,只不过其中的value值都是null值,而HashMap的底层是用数组实现的。
Java多线程编程
这里要先回忆一下进程,即运行中的程序,多任务操作系统中并发的一个任务(CPU是分时间片执行多个进程的),线程,其本质是进程中顺序的执行流程,进程有独立的进程空间进程中的数据存放空间(对空间和栈空间)是独立的。线程没有独立的存放数据的空间,他们的数据存储空间(堆空间)是共享的,线程间的栈空间是独立的,线程消耗的资源也比进程小。
线程,是进程(运行中的程序)中顺序的执行流程,进程可以划分出多个线程。
JVM(java虚拟机)本身就是一个进程,java只能够申请创建线程。
操作系统决定线程是否有优先级,独占式的操作系统中系统会有优先级的概念,共享式的操作系统则不会优先级的。
Java中的线程也是对象,线程类是Thread类,线程是操作系统创建并进行维护的资源,线程对象只能表示一个线程,他本身不是线程。只能通过线程对象来申请创建,中止线程,访问底层的线程资源。
线程包含了三部分,cpu资源,代码,数据存储空间。
线程对象调用start()方法,线程对象就会向操作系统申请线程启动,除了申请的线程,还有main方法的线程,也就是主线程。
注意:只有运行状态的线程才有机会执行代码,主线程的中止不会影响其他的正在运行中的线程,朱线程中止也就是main()方法退出了。只有进程中的所有线程都中止时,进程(JVM进程)才会退出,只要有线程没有中止,进程就不会退出。
线程编程的两种方法
1,写一个类,继承Thread类,覆盖Thread类中继承来的run()方法,这样就写好了自定义的线程类。
2,写一个类,实现Runable接口,实现其中的run()方法。这种方法写好的类的对象需要作为线程类创建对象时构造方法的参数。
1,
Thread t=new a();
class a extends Thread{
       public void run(){
              ...
       }
}
2,
Runable r=new b();//目标对象
Thread t=new Thread(r);//用目标对象构造线程对象
class b implements Runable{
       public void run(){
              ...
       }
}
Thread的方法
 
public static void sleep(long millis)
                  throws InterruptedException
括号中以毫秒为单位, 使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。
当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。
       try{
              Thread.sleep(1000);
       }catch(InterruptedException e){
              e.printStackTrace(e);
       }
public final void join() throws InterruptedException
表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。
实际上是把并发的线程变为串行运行。
 
线程的优先级:1-10,越大优先级越高,优先级越高被OS选中的可能性就越大。(不建议使用,因为不同操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。
注:程序的跨平台性:除了能够运行,还必须保证运行的结果。
 
Public static void field()
使当前线程马上交出执行权,回到可运行状态,等待OS的再次调用。
 
Public final Boolean isActive()
验证当前线程是否是活动的,不管它是否正在运行。
 
线程的生命周期
 
下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)
1,初始状态,线程创建,线程对象调用start()方法。
2,可运行状态,也就是等待Cpu资源,等待运行的状态。
3,运行状态,获得了cpu资源,正在运行状态。
4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。
1)如等待输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。
2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。
3)线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。
5,中止状态,也就是执行结束。
6,锁池状态
7,等待队列
 
共享数据的并发处理
 
多线程同时并发访问的资源叫做临界资源。
 
多个线程同时访问对象并要求操作相同资源时分割了原子操作就会出现问题。(原子操作,不可再分的操作)会出现数据的不一致或数据不完整,为避免这种现象采用对访问的线程做限制的方法。
 
互斥锁机制,利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个琐是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程。
 
1.Synchronized修饰代码块(同步代码块),
   public void push(char c){
        synchronized(this)//只有持有当前对象的锁标记才能访问这个代码块
       {
                 ...
        }
   }
 
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2.Synchronized修饰方法
   public synchronized void push(char c) {
      ...
      }
对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。
 
注意:构造方法不能Synchronized修饰,静态方法可以用Synchronized修饰(是对类对象加锁,类对象会在反射时讲到)。抽象方法不能用Synchronized修饰,不影响子类覆盖,子类在覆盖这个方法是可以加Synchronized,也可以不加Synchronized,所以根据Java不允许写废代码的特点是不能写在一起。
 
注意:对当前对象加锁,一个代码块或者方法是同步的(Synchronized),当前对象的锁标记没有分配出去时,有一个线程来访问这个代码块时,就会的到这个对象的锁标记,直到这个线程结束才会释放着个锁标记,其他想访问这个代码块或者是方法线程就会进入这个对象锁池,如果没有得到当前对象的锁标记,就不能访问这个代码块或者是方法。当一个线程想要获得某个对象锁标记而进入锁池,这个线程又持有其他对象的锁标记,那么这个线程也不会释放持有的锁标记。
 
注:方法的Synchronized特性本身不会被继承,只能覆盖。
线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。
 
使用互斥锁的注意事项
 
举例:男孩和女孩例子,每个女孩是一个对象,每个男孩是个线程。每个女孩都有自己的锁池。每个男孩可能在锁池里等待。
Class Girl{
       Public void hand(){
 
       }
       Public syncronized void kiss(){
 
       }
}
Class Boy extends Thread{
       Public void run(){
             
       }
}
 
注意:只读不用加同步,只写也不用加同步,只有读写操作兼而有之时才加同步。
 
注意:在java.io包中Vector 和 HashTable 之所以是线程安全的,是因为每个方法都有synchronized修饰。Static 方法可以加 synchronized , 锁的是类对象。但是Vector 是 jdk 1.0 的 ArrayList 是 jdk1.2 所以实际应用还是使用ArrayList。
 
注意:内同步,外同步,内同步,即,类内的方法加同步(synchronized)修饰,外同步即,在需要控制只能由一个线程进行访问时,把需要控制的方法写在同步代码块里。
 
死锁问题
 
多线程不释放自己拥有的锁标记,而想申请其他线程拥有的锁标记,就会造成死锁。
 
没有获得加锁对象的锁标记的线程,不能访问只有获得该对象所标记才能访问的同步方法,但可以访问这个对象的非同步的方法。
 
死锁的两种处理方法
统一排列锁顺序(解决不同方法中对多个共享资源的访问)
       对象1的方法
              synchronized(a)
              synchronized(b)
       对象2的方法
              synchronized(a)
              synchronized(b)
 
java线程间通信(也就是线程间的相互协调)
 
等待通知机制
 
线程间通信使用的空间称之为对象的等待对列(wait pool),该队列也是属于对象的空间的。
 
进入等待池
 
使用Object类中wait()的方法,在运行状态中,线程调用wait(),此时表示线程将释放自己所有的锁标记和CPU的占用,同时进入这个对象的等待池。等待池的状态也是阻塞状态,只不过线程释放自己的锁标记。只有在对该对象加锁的同步代码块里,才能掉用该对象的wait(),表示线程将会释放所有锁标记,进入等待队列,线程将进入等待队列状态。
 
一个线程进入了对一个对象加锁的同步代码块,并对该对象调用了wait()方法,释放自己拥有的所有锁标记,进入该对象等待队列,另一个线程获得了该对象的锁标记,进入代码块对该对象调用了notify()方法(如果对该对象调用了notifyAll()方法,就会使放等待队列里所有的线程),对该对象调用方法的线程也不会释放所拥有的锁标记(对自身没有影响),也就是从等待队列里释放出一线程,释放出的这个线程要继续运行也就还要进入那个同步代码块,因为得不到要访问代码块对象的锁标记,而进入该对象的锁池,等待所标记释放。
 
注意:用notifyAll()取代notify(),因为在调用notify()方法时,是由系统决定释放出哪个线程。
退出等待池进入锁池
 
notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
 
注意:只能对加锁的资源进行wait()和notify()。
1) wait():交出锁和CPU的占用;
2) notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
3) notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
网络编程
 
网络基础知识
 
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。
计算机网络形式多样,内容繁杂。网络上的计算机要互相通信,必须遵循一定的协议。目前使用最广泛的网络协议是Internet上所使用的TCP/IP协议。
IP地址:计算机在网络中唯一标识,相对于internet,IP为逻辑地址。
 
IP地址分类:
A类地址
A类地址第1字节为网络地址,其它3个字节为主机地址。另外第1个字节的最高位固定为0。
A类地址范围:1.0.0.1到126.155.255.254。
A类地址中的私有地址和保留地址:
10.0.0.0到10.255.255.255是私有地址(所谓的私有地址就是在互联网上不使用,而被用在局域网络中的地址)。
127.0.0.0到127.255.255.255是保留地址,用做循环测试用的。
 
B类地址
 
B类地址第1字节和第2字节为网络地址,其它2个字节为主机地址。另外第1个字节的前两位固定为10。
B类地址范围:128.0.0.1到191.255.255.254。
B类地址的私有地址和保留地址
172.16.0.0到172.31.255.255是私有地址
169.254.0.0到169.254.255.255是保留地址。如果你的IP地址是自动获取IP地址,而你在网络上又没有找到可用的DHCP服务器,这时你将会从169.254.0.0到169.254.255.255中临得获得一个IP地址。
 
C类地址
C类地址第1字节、第2字节和第3个字节为网络地址,第4个个字节为主机地址。另外第1个字节的前三位固定为110。
C类地址范围:192.0.0.1到223.255.255.254。
C类地址中的私有地址:
192.168.0.0到192.168.255.255是私有地址。
 
D类地址
D类地址不分网络地址和主机地址,它的第1个字节的前四位固定为1110。
D类地址范围:224.0.0.1到239.255.255.254
 
Mac地址:每个网卡专用地址,也是唯一的。
端口(port):应用程序(进程)的标识(网络通信程序)OS中可以有65536(2^16)个端口,进程通过端口交换数据。连线的时候需要输入IP也需要输入端口信息。
计算机通信实际上的主机之间的进程通信,进程的通信就需要在端口进行联系。
192.168.0.23:21
协议:为了进行网络中的数据交换(通信)而建立的规则、标准或约定,协议是为了保证通信的安全。
不同层的协议是完全不同的。
 
网络层:寻址、路由(指如何到达地址的过程)
传输层:端口连接
TCP模型:应用层/传输层/网络层/网络接口
层与层之间是单向依赖关系,上层依赖于下层,下层不依赖于上层,层与层之间的连接是虚连接。对等层之间建立协议。
端口是一种抽象的软件结构,与协议相关:TCP23端口和UDT23端口为两个不同的概念。
端口应该用1024以上的端口,以下的端口都已经设定功能。
 
TCP/IP模型
 
Application
(FTP,HTTP,TELNET,POP3,SMPT)
Transport
(TCP,UDP)
Network
(IP,ICMP,ARP,RARP)
Link
(Device driver,….)
 
注:
IP:寻址和路由
ARP(Address Resolution Protocol)地址解析协议:将IP地址转换成Mac地址
RARP(Reflect Address Resolution Protocol)反相地址解析协议:与上相反
ICMP(Internet Control Message Protocol)检测链路连接状况。利用此协议的工具:ping , traceroute
 
TCP Socket
 
TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
   1) 服务器分配一个端口号,服务器使用accept()方法等待客户端的信号,信号一到打开socket连接,从socket中取得OutputStream和InputStream。
   2) 客户端提供主机地址和端口号使用socket端口建立连接,得到OutputStream和InputStream。
TCP/IP的传输层协议
 
      
 
建立TCP服务器端
 
一般,我们把服务器端写成是分发Socket的,也就是总是在运行,
创建一个TCP服务器端程序的步骤:
   
    1). 创建一个ServerSocket
    2). 从ServerSocket接受客户连接请求
    3). 创建一个服务线程处理新的连接
    4). 在服务线程中,从socket中获得I/O流
    5). 对I/O流进行读写操作,完成与客户的交互
    6). 关闭I/O流
    7). 关闭Socket
ServerSocket server = new ServerSocket(post)
Socket connection = server.accept();
ObjectInputStream put=new ObjectInputStream(connection.getInputStream());
ObjectOutputStreamo put=newObjectOutputStream(connection.getOutputStream());   
处理输入和输出流;
关闭流和socket。
典型的服务器端。
public class Server1 {
       public static void main(String[] args) throws Exception {
              ServerSocket ss=new ServerSocket(9000);
              while(true){
                     Socket s=ss.accept();//获得一个Socket对象。
                     Thread t=new Thread1(s);//分发Socket。
                     t.start();
              }
       }
}
class Thread1 extends Thread{
       Socket s;
       public Thread1(Socket s){
              this.s=s;
       }
       public void run(){
              try {
                     OutputStream o=s.getOutputStream();
                     PrintWriter out=new PrintWriter(o);
                     out.println("Hello Client");
                     out.flush();
                     s.close();
              } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }           
       }
}
建立TCP客户端
 
创建一个TCP客户端程序的步骤:
 
    1).创建Socket
    2). 获得I/O流
    3). 对I/O流进行读写操作
    4). 关闭I/O流
    5). 关闭Socket
Socket connection = new Socket(127.0.0.1, 7777);
ObjectInputStream input=new ObjectInputStream(connection.getInputStream());
ObjectOutputStream utput=new ObjectOutputStream(connection.getOutputStream());
处理输入和输出流;
关闭流和socket。
 
 
 
UDP socket
这种信息传输方式相当于传真,信息打包,在接受端准备纸。
特点:
1) 基于UDP无连接协议
2) 不保证消息的可靠传输
3) 它们由Java技术中的DatagramSocket和DatagramPacket类支持
DatagramSocket(邮递员):对应数据报的Socket概念,不需要创建两个socket,不可使用输入输出流。
DatagramPacket(信件):数据包,是UDP下进行传输数据的单位,数据存放在字节数组中,其中包括了目标地址和端口以及传送的信息(所以不用建立点对点的连接)。
DatagramPacket的分类:
用于接收:DatagramPacket(byte[] buf,int length)
                 DatagramPacket(byte[] buf,int offset,int length)
用于发送:DatagramPacket(byte[] buf,int length, InetAddress address,int port )
                 DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)
注:InetAddress类网址用于封装IP地址
没有构造方法,通过
InetAddress.getByAddress(byte[] addr):InetAddress
InetAddress.getByName(String host):InetAddress
等。
 
建立UDP 发送端
 
创建一个UDP的发送方的程序的步骤:
 
    1). 创建一个DatagramPacket,其中包含发送的数据和接收方的IP地址和端口
号。
    2). 创建一个DatagramSocket,其中包含了发送方的IP地址和端口号。
    3). 发送数据
    4). 关闭DatagramSocket
byte[] buf = new byte[1024];
DatagramSocket datagramSocket = new DatagramSocket(13);// set port
DatagramPackage intputPackage = new DatagramPackage(buf,buf.length);
datagramSocket.receive(inputPackage);
DatagramPackage outputPackage = new DatagramPackage(buf,buf.length,
inetAddress,port);
datagramSocket.send(outputPackage);
没建立流所以不用断开。
 
建立UDP 接受端
 
创建一个UDP的接收方的程序的步骤:
 
    1). 创建一个DatagramPacket,用于存储发送方发送的数据及发送方的IP地址和端口号。
    2). 创建一个DatagramSocket,其中指定了接收方的IP地址和端口号。
    3). 接收数据
    4). 关闭DatagramSocket
byte[] buf = new byte[1024];
DatagramSocket datagramSocket = new DatagramSocket();//不用设端口,因为发送的包中端口
DatagramPackage outputPackage=new DatagramPackage(
Buf,buf.length,serverAddress,serverPort);
DatagramPackage inputPackage=new DatagramPackage(buf,buf.length);
datagramSocket.receive(inputPackage);
反射
 
反射,在运行时,动态分析或使用一个类进行工作。
类对象,是一个描述这个类信息的对象,对虚拟机加载类的时候,就会创建这个类的类对象并加载该对象。Class,是类对象的类型。只有对象才会被加载到虚拟机中。一个类只会被加载一次。
 
获得类对象的三种方式:(类对象不用new的方法得到的)
 
1)也可以用 类名.Class,获得这个类的类对象。
2)用一类的对象掉用a.getClass(),得到这个对象的类型的类对象。
3)也可以使用Class.forName(类名)(Class类中的静态方法),也可以得到这个类的类对象,(注意,这里写的类名必须是全限定名(全名),是包名加类名,XXX.XXX.XXXX)。强制类加载,这种方法是经常使用的。
 
一个类的类对象是唯一的。
在使用Class.forName(类名)时,如果使用时写的类名的类,还没有被加载,则会加载这个类。
Class c,c.getSuperclass()这个方法是获得这个类的父类的类对象。c.getInterfaces()会获得这个类所实现的接口,这个方法返回是一个类对象的数组。
 
方法对象是类中的方法的信息的描述。java.lang.reflect.Method,方法类的对象可以通过类对象的getMethods() 方法获得,获得的是一个方法对象的数组,获得类中的定义的所有方法对象,除了构造方法。
 
构造方法对象,是用来描述构造方法的信息。java.lang.reflect.Constructor构造方法类的对象可以通过类对象的getConstructors()方法获得,获得这个类的所有构造方法对象。
 
属性对象,使用来描述属性的信息。java.lang.reflect.Field属性类的对象对象可以通过类对象getFields() 这个方法是获得所有属性的属性对象。
 
 
反射可以获取这个类中定义的方法和属性的信息,简单数据类型在使用反射时要转换成封装类。
 
反射可以通过类对象操纵类,生成这个类的对象,调用方法等等。
 
使用反射构造一个类的对象的步骤
 
1,获得类对象(类对象是对类的信息的描述)
2,获得构造方法对象(方法对象是类中的方法的信息的描述)
3,获得对象(用构造方法对象掉用构造方法,如果是使用无参构造方法,可以跳过第2部,直接使用类对象.newInstance() 方法来获得这个类的对象)
4,获得方法对象。
5,用方法对象调用方法(用这个类的对象作为第一参数)。
 
这里在调用方法时,方法的参数表,是用用类对象数组表示参数类型的。
 
其他有关于反射的方法可以查阅API文档。java.lang.Class,java.lang.reflce包
 
反射提供了最大的通用性,反射可以绕过编译器,反射可以访问对象的私有属性和方法。
 
import java.lang.reflect.*;
public class TestReflect {
       public static void main(String[] args) throws Exception {
             
              //1.get class Object
              Class c=Class.forName("Student");
              //2.get Constructor object
              Class[] cs={String.class};
              Constructor con=c.getConstructor(cs);//按照参数表来调用制定构造方法。
              //3.create object
              Object[] os={"liucy"};
              Object o=con.newInstance(os);
              //4.get method object
              String methodName="study";
              Class[] pcs={String.class};
              Method m=c.getMethod(methodName,pcs);//按照参数表来获得制定的方法对象。
              //5.invoke the method
              Object[] ocs={"EJB"};
              m.invoke(o,ocs);
             
       }
 
}
 
class Student{
       private String name;
       public Student(String n){
              this.name=n;
       }
       public void study(String course){
              System.out.println(name+" studies "+course);
       }
       public void eat(String food){
              System.out.println(name+" eats "+food);
       }
}
 
class Teacher{
       String name;
       public Teacher(String n){
              this.name=n;
       }
       public void teach(String course){
              System.out.println(name+" teaches "+course);
       }
}
 
java5.0的新特性
 
自动装箱和自动拆箱
 
自动封箱和自动拆箱,它实现了简单类型和封装类型的相互转化时,实现了自动转化。
 
自动封箱解箱只在必要的时候才进行。还有其它选择就用其它的
byte b      -128~127
Byte b      多一个null
 
简单类型和封装类型之间的差别
封装类可以等于null ,避免数字得0时的二义性。
Integer i=null;
int ii=i; 会抛出NullException 异常。
相当于 int ii=i.intValue();
Integer i=1;相当于Integer i=new Integer(1);
 
在基本数据类型和封装类之间的自动转换
5.0之前
Integer i=new Integer(4);
int ii= i.intValue();
 
5.0之后
Integer i=4;
Long l=4.3;
 
静态引入
 
静态成员的使用,使用import static 引入静态成员,也就是可以用静态引入是导入包中的某个类的静态成员,在使用时不用再写类名。
很简单的东西,看一个例子:
 
没有静态导入
 
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
 
有了静态导入
 
import static java.lang.Math.*;
 
sqrt(pow(x, 2) + pow(y, 2));
 
其中import static java.lang.Math.*;就是静态导入的语法,它的意思是导入Math类中的所有static方法和属性。这样我们在使用这些方法和属性时就不必写类名。
 
需要注意的是默认包无法用静态导入,另外如果导入的类中有重复的方法和属性则需要写出类名,否则编译时无法通过。
 
增强的for循环
 
for-each循环实现了对数组和集合的便利的统一,解决遍历数组和遍历集合的不统一。
 
import java.util.*;
import java.util.Collection;
 
public class Foreach
{
private Collection<String> c = null;
    private String[] belle = new String[4];
    public Foreach()
    {
          belle[0] = "西施";
               belle[1] = "王昭君";
               belle[2] = "貂禅";
               belle[3] = "杨贵妃";
               c = Arrays.asList(belle);
    }
    public void testCollection(){
        for (String b : c){
              System.out.println("曾经的风化绝代:" + b);
        }
    }
    public void testArray(){
        for (String b : belle){
              System.out.println("曾经的青史留名:" + b);
        }
    }
    public static void main(String[] args){
        Foreach each = new Foreach();
        each.testCollection();
        each.testArray();
    }
}
 
 
对于集合类型和数组类型的,我们都可以通过foreach语法来访问它。上面的例子中,以前我们要依次访问数组,挺麻烦:
 
for (int i = 0; i < belle.length; i++){
        String b = belle[i];
        System.out.println("曾经的风化绝代:" + b);
}
 
 
现在只需下面简单的语句即可:
 
for (String b : belle){
       System.out.println("曾经的青史留名:" + b);
 }
 
对集合的访问效果更明显。以前我们访问集合的代码:
for (Iterator it = c.iterator(); it.hasNext();){
        String name = (String) it.next();
        System.out.println("曾经的风化绝代:" + name);
}
 
现在我们只需下面的语句:
for (String b : c){
        System.out.println("曾经的风化绝代:" + b);
}
 
Foreach也不是万能的,它也有以下的缺点:
 
在以前的代码中,我们可以通过Iterator执行remove操作。
 
for (Iterator it = c.iterator(); it.hasNext();){
       it.remove();
}
 
但是,在现在的for-each版中,我们无法删除集合包含的对象。你也不能替换对象。
 
同时,你也不能并行的for-each多个集合。所以,在我们编写代码时,还得看情况而使用它。
 
 
 
可变长的参数
使用条件:只在必要的时候进行。同时有参数为数组,就不能使用变长参数,有变长参数,就不能使用数组,不能共存。一个方法最多只能有一个变长参数,而且是最后一个参数。
 
5.0之前
public static void main(String[] args){
 
}
JVM收到数据封装在数组里,然后传入方法
5.0之后
public static void m(String... s){
       System.out.println("m(String)" +s);
}
调用m(String… s)
for(String s2:s){
       System.out.println(s2);
}
 
 
格式化输出
 
格式化I/O(Formatted I/O)
java.util.Sacner类可以进行格式化的输入,可以使用控制台输入,结合了BufferedReader和StringTokener的功能。
 
增加了类似C的格式化输入输出,简单的例子:
 
public class TestFormat{
 
 public static void main(String[] args){
 
    int a = 150000, b = 10;
 
    float c = 5.0101f, d = 3.14f;
 
   
 
    System.out.printf("%4d %4d%n", a, b);
 
    System.out.printf("%x %x%n", a, b);
 
    System.out.printf("%3.2f %1.1f%n", c, d);
 
    System.out.printf("%1.3e %1.3e%n", c, d*100);
 
 }
 
}
 
输出结果为:
 
150000    10
 
249f0 a
 
5.01 3.1
 
5.010e+00 3.140e+02
 
 
类型安全的枚举
 
枚举也是一个类型,枚举中的对象只能定义一次并在定义时给其初始化,定义之后不能再改变其值,只能从枚举中选择其一。
 
enum 枚举名{
       枚举值1(..),枚举值2(..),.....;
}
 
在5.0之前使用模式做出枚举
final class Season{
       public static final Season SPRING=new Season();
       public static final Season WINTER=new Season();
       public static final Season SUMMER=new Season();
public static final Season AUTUMN=new Season();
private Season(){}
}
完全等价于
enum Season2{
       SPRING(..),//枚举值
       SUMMER(..),
       AUTUMN(..),
       WINTER(..);
......
}
 
枚举是一个反射关联的典型,反射关联,即在类的定义中有自身类型的属性。
枚举本质上也是一个类,Enum是枚举的父类。
枚举中的values()方法会返回枚举中的所有枚举值
枚举中可以定义方法和属性,最后的一个枚举值要以分号和类定义分开,枚举中可以定义的构造方法。
枚举可以实现接口,枚举不能有子类也就是final的,枚举的构造方法是private(私有的),枚举中可以定义抽象方法,可以在枚举值的值中实现抽象方法,枚举值就是枚举的对象,枚举默认是final,枚举值可以隐含的匿名内部类来实现枚举中定义抽象方法。
 
 
枚举类(Enumeration Classes)和类一样,具有类所有特性。Season2的父类是java.lang.Enum;
隐含方法:    Season2[] ss=Season2.values(); 每个枚举类型都有的方法。enum可以switch中使用(不加类名)。
switch( s ){
       case SPRING:
                     …………….
       case SUMMER:
                     …………….
       …………..
}
 
枚举的有参构造
enum Season2{
       SPRING(“春”),-------------------------------逗号
       SUMMER(“夏”),-------------------------------逗号
       AUTUMN(“秋”),-------------------------------逗号
       WINTER(“冬”);-------------------------------分号
       private String name;
       Season2(String name){
              this.name=name;
}
String getName(){
       return name;
}
 
}
Season2.SPRING.getName()          ---------------------春
 
 
枚举的高级用法:
enum Operation{
       ADD{
              public double calculate(double s1,double s2){
                     return s1+s2;
              }
       },
       SUBSTRACT{
              public double calculate(double s1,double s2){
                     return s1-s2;
              }
       },
       MULTIPLY{
              public double calculate(double s1,double s2){
                     return s1*s2;
              }
       },
       DIVIDE{
              public double calculate(double s1,double s2){
                     return s1/s2;
              }
       };
       public abstract double calculate(double s1 ,double s2);
}
有抽象方法枚举元素必须实现该方法。
 
java5.0中的泛型
 
说明
 
增强了java的类型安全,可以在编译期间对容器内的对象进行类型检查,在运行期不必进行类型的转换。而在java se5.0之前必须在运行期动态进行容器内对象的检查及转换,泛型是编译时概念,运行时没有泛型
 
减少含糊的容器,可以定义什么类型的数据放入容器
 
 
 List<Integer> aList = new ArrayList<Integer>();
 
    aList.add(new Integer(1));
 
    // ...
 
    Integer myInteger = aList.get(0);
支持泛型的集合,只能存放制定的类型,或者是指定类型的子类型。
 
我们可以看到,在这个简单的例子中,我们在定义aList的时候指明了它是一个只接受Integer类型的ArrayList,当我们调用aList.get(0)时,我们已经不再需要先显式的将结果转换成Integer,然后再赋值给myInteger了。而这一步在早先的Java版本中是必须的。也许你在想,在使用Collection时节约一些类型转换就是Java泛型的全部吗?远不止。单就这个例子而言,泛型至少还有一个更大的好处,那就是使用了泛型的容器类变得更加健壮:早先,Collection接口的get()和Iterator接口的next()方法都只能返回Object类型的结果,我们可以把这个结果强制转换成任何Object的子类,而不会有任何编译期的错误,但这显然很可能带来严重的运行期错误,因为在代码中确定从某个Collection中取出的是什么类型的对象完全是调用者自己说了算,而调用者也许并不清楚放进Collection的对象具体是什么类的;就算知道放进去的对象“应该”是什么类,也不能保证放到Collection的对象就一定是那个类的实例。现在有了泛型,只要我们定义的时候指明该Collection接受哪种类型的对象,编译器可以帮我们避免类似的问题溜到产品中。我们在实际工作中其实已经看到了太多的ClassCastException。
 
用法
 
声明及实例化泛型类:
 
HashMap<String,Float> hm = new HashMap<String,Float>();
编译类型的泛型和运行时类型的泛型一定要一致。没有多态。
不能使用原始类型
 
GenList<int> nList = new GenList<int>(); //编译错误
 
Java SE 5.0目前不支持原始类型作为类型参数(type parameter)
 
定义泛型接口:
 
public interface GenInterface<T> {
      void func(T t);
}
 
定义泛型类:
 
public class ArrayList<ItemType> { ... }
 
public class GenMap<T, V> { ... }
 
例1:
 
public class MyList<Element> extends LinkedList<Element>
{
    public void swap(int i, int j){
        Element temp = this.get(i);
        this.set(i, this.get(j));
        this.set(j, temp);
    }
 
    public static void main(String[] args){
        MyList<String> list = new MyList<String>();
        list.add("hi");
        list.add("andy");
        System.out.println(list.get(0) + " " + list.get(1));
        list.swap(0,1);
        System.out.println(list.get(0) + " " + list.get(1));
    }
 
}
 
泛型的通配符"?"
 
? 是可以用任意类型替代。
<?>泛型通配符表示任意类型
<? extends 类型>表示这个类型是某个类型的子类型。
<? super 类型>表示这个类型是某个类型的父类型。
 
import java.util.*;
import static java.lang.System.*;
public class TestTemplate {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              List<String> l1=new ArrayList<String>();
              l1.add("abc");
              l1.add("def");
              List<Number> l2=new ArrayList<Number>();
              l2.add(1.3);
              l2.add(11);
              List<Integer> l3=new ArrayList<Integer>();
              l3.add(123);
              l3.add(456);
             
       //     print(l1);
              print(l2);
              print(l3);
       }
       static void print(List<? extends Number> l){ //所有Number的子类
              for(Object o:l){
                     out.println(o);
              }
       }
 
static void print(List<? super Number> l){    //所有Number的父类
              for(Object o:l){
                     out.println(o);
              }
       }
 
}
 
"?"可以用来代替任何类型, 例如使用通配符来实现print方法。
 
public static void print(GenList<?> list) {})
 
泛型方法的定义
 
把数组拷贝到集合时,数组的类型一定要和集合的泛型相同。
<...>定义泛型,其中的"..."一般用大写字母来代替,也就是泛型的命名,其实,在运行时会根据实际类型替换掉那个泛型。
 
<E> void copyArrayToList(E[] os,List<E> lst){
              for(E o:os){
                     lst.add(o);
              }
       }
 
static <E extends Number> void copyArrayToList(E[] os,List<E> lst){
              for(E o:os){
                     lst.add(o);
              }
       }
 
static<E extends Number & Comparable> void copyArrayToList(E[] os,List<E> lst){
              for(E o:os){
                     lst.add(o);
              }
       }
 
受限泛型是指类型参数的取值范围是受到限制的. extends关键字不仅仅可以用来声明类的继承关系, 也可以用来声明类型参数(type parameter)的受限关系.例如, 我们只需要一个存放数字的列表, 包括整数(Long, Integer, Short), 实数(Double, Float), 不能用来存放其他类型, 例如字符串(String), 也就是说, 要把类型参数T的取值泛型限制在Number极其子类中.在这种情况下, 我们就可以使用extends关键字把类型参数(type parameter)限制为数字
只能使用extends不能使用 super,只能向下,不能向上。
调用时用<?>定义时用 <E>
 
泛型类的定义
 
类的静态方法不能使用泛型,因为泛型类是在创建对象的时候产生的。
 
class MyClass<E>{
       public void show(E a){
              System.out.println(a);
       }
       public E get(){
              return null;
       }
      
}
 
受限泛型
 
class MyClass <E extends Number>{
       public void show(E a){
 
       }
}
 
泛型与异常
 
类型参数在catch块中不允许出现,但是能用在方法的throws之后。例:
 
import java.io.*;
 
interface Executor<E extends Exception> {
    void execute() throws E;
}
 
public class GenericExceptionTest {
    public static void main(String args[]) {
        try {
              Executor<IOException> e = new Executor<IOException>() {
                  public void execute() throws IOException{
                      // code here that may throw an
                      // IOException or a subtype of
                      // IOException
                  }
                 };
              e.execute();
        } catch(IOException ioe) {
              System.out.println("IOException: " + ioe);
              ioe.printStackTrace();
        }
    }
}
 
 
 
泛型的一些局限型
 
catch不能使用泛型,在泛型集合中,不能用泛型创建对象,不允许使用泛型的对象。
 
不能实例化泛型
 
T t = new T(); //error
 
不能实例化泛型类型的数组
 
T[] ts= new T[10];    //编译错误
 
不能实例化泛型参数数
 
Pair<String>[] table = new Pair<String>(10); // ERROR
 
类的静态变量不能声明为类型参数类型
 
public class GenClass<T> {
 private static T t;    //编译错误
}
静态方法可以是泛型方法,但是不可以使用类的泛型。
 
泛型类不能继承自Throwable以及其子类
 
public GenExpection<T> extends Exception{}    //编译错误
 
不能用于基础类型int等
 
Pair<double> //error
 
Pair<Double> //right
 
JAVA5.0 的注释 (Annotation)
 
描述代码的代码。给编译器看的代码,作用是规范编译器的语法。
class Student{
       @Override
       public String toString(){
              return “student”;
       }
 
}
类型(接口)
1.标记注释
@Override
2.单值注释
 
@注释名(prameter=10)
int parameter
特例:
@注释名 (value “134” )
 
@SuperessWarning({“ddd”,”aaa”,”ccc”}) //JVM还没有实现这个注释
3.普通注释(多值注释)
(key1=value,……)
 
4.自定义注释
public @interface Test{
 
}
在自定义注释时,要用注释来注释(描述)注释。
@target(),用来描述(注释)注释所能够注释的程序员元素。
@Retention(),描述(注释)注释要保留多久。
注释的属性类型可以是
8种基本类型
String
Enum
Annotation
以及它们的数组
 
三个新加的多线程包
 
Java 5.0里新加入了三个多线程包:java.util.concurrent, java.util.concurrent.atomic,
 
java.util.concurrent.locks.
java.util.concurrent包含了常用的多线程工具,是新的多线程工具的主体。
java.util.concurrent.atomic包含了不用加锁情况下就能改变值的原子变量,比如说AtomicInteger提供了addAndGet()方法。Add和Get是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。
java.util.concurrent.locks包包含锁定的工具。
 
Callable 和 Future接口
 
Executor接口替代了Thread类,他可以创建定量的和动态以及周期性的线程池。
ExecutorService接口,线程池,用来存放线程来节省创建和销毁资源的消耗。
 
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。Callable和Runnable有几点不同:
Callable规定的方法是call(),而Runnable规定的方法是run().
Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
call()方法可抛出异常,而run()方法是不能抛出异常的。
 
Future对象可以获得线程运行的返回值
 
运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
以下是Callable的一个例子:
public class DoCallStuff implements Callable{ // *1
        private int aInt;
        public DoCallStuff(int aInt) {
                this.aInt = aInt;
        }
        public String call() throws Exception { //*2
                boolean resultOk = false;
                if(aInt == 0){
                        resultOk = true;
                } else if(aInt == 1){
                        while(true){ //infinite loop
                                System.out.println("looping....");
                                Thread.sleep(3000);
                        }
                } else {
                        throw new Exception("Callable terminated with Exception!"); //*3
                }
                if(resultOk){
                        return "Task done.";
                } else {
                        return "Task failed";
                }
        }
}
*1: 名为DoCallStuff类实现了Callable,String将是call方法的返回值类型。例子中用了String,但可以是任何Java类。
*2: call方法的返回值类型为String,这是和类的定义相对应的。并且可以抛出异常。
*3: call方法可以抛出异常,如加重的斜体字所示。
以下是调用DoCallStuff的主程序。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Executor {
        public static void main(String[] args){
                //*1
                DoCallStuff call1 = new DoCallStuff(0);
                DoCallStuff call2 = new DoCallStuff(1);
                DoCallStuff call3 = new DoCallStuff(2);
                 //*2
                ExecutorService es = Executors.newFixedThreadPool(3);
                //*3
                Future future1 = es.submit(call1);
                Future future2 = es.submit(call2);
                Future future3 = es.submit(call3);
                try {
                        //*4
                        System.out.println(future1.get());
                         //*5
                        Thread.sleep(3000);
                        System.out.println("Thread 2 terminated? :" + future2.cancel(true));
                        //*6
                        System.out.println(future3.get());
                } catch (ExecutionException ex) {
                        ex.printStackTrace();
                } catch (InterruptedException ex) {
                        ex.printStackTrace();
                }
        }
}
*1: 定义了几个任务
*2: 初始了任务执行工具。任务的执行框架将会在后面解释。
*3: 执行任务,任务启动时返回了一个Future对象,如果想得到任务执行的结果或者是异常可对这个Future对象进行操作。Future所含的值必须跟Callable所含的值对映,比如说例子中Future对印Callable
*4: 任务1正常执行完毕,future1.get()会返回线程的值
*5: 任务2在进行一个死循环,调用future2.cancel(true)来中止此线程。传入的参数标明是否可打断线程,true表明可以打断。
*6: 任务3抛出异常,调用future3.get()时会引起异常的抛出。
 运行Executor会有以下运行结果:
looping....
Task done. //*1
looping....
looping....//*2
looping....
looping....
looping....
looping....
Thread 2 terminated? :true //*3
//*4
java.util.concurrent.ExecutionException: java.lang.Exception: Callable terminated with Exception!
        at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:205)
        at java.util.concurrent.FutureTask.get(FutureTask.java:80)
        at concurrent.Executor.main(Executor.java:43)
        …….
*1: 任务1正常结束
*2: 任务2是个死循环,这是它的打印结果
*3: 指示任务2被取消
*4: 在执行future3.get()时得到任务3抛出的异常
 
lock接口
 
实现类ReentrantLock
 
我们可以用lock对象,来对临界资源加锁,只有获得lock对象才能访问临界资源,如果没有获得lock对象,就会进入lock对象的锁池。trylock()方法会返回布尔值,这个方法是用来判断这个锁对象是不是已经被线程获取,如果返回值为true,则会直接获得这个锁对象,如果返回false,线程不会阻塞还会继续运行。
Lock lock=new ReentrantLock();
publci void test(){
   try{
       lock.lock();//加锁
       .....//需要加锁的临界资源。
       }finally{
              lock.unlock();//解锁。
       }
}
 
ReadWriteLock读写锁接口
 
ReentrantReadWriteLock是ReadWriteLock的实现类。
 
readLock()分配读锁,读锁可以分配多个线程,但是在分配读锁后所有读锁释放前,写锁时不能分配的。
Lock writeLock() 写锁只能分配给一个线程,在分配写锁后写锁是放前,读锁不能被分配。
 
Condition接口和实现类
 
await()替代了wait()方法。
notify(),notifyAll() 在JDK5.0中已经用signal() ,signalAll()方法替换掉了,在JDK5.0中,可以使用多个等待队来存放等待的线程,并对线程进行分类。
 
Queue接口(Collection的子接口,对列接口)
 
LinkedList也实现了这个在JDK5.0中的新接口Queue,并且这个类自动的实现了生产者和消费者的同步。
 
 
JDK5.0的高级同步
Semaphore类(信号量)也就是可以向线程分配许可证,指定许可证数量可以实现多线程的同步。
Semaphore s=new Semaphore(4);//可以分配4个许可证,许可证都被分配出去时,得不到许可证的线程就会阻塞。
 
acquire()方法,获得许可证。release() 方法,释放一个许可证,也有相应的方法指定释放和获得许可证的数量的方法。
 
CountDownLatch类,
 
CountDownLatch中有个计数器,访问这个类的对象就会从计数器中减一,countDown()方法会将原有的设置的计数器值减一,当countdown计数器为零时会使放所有await()的线程。
 
CyclicBarrier类
 
CyclicBarrier和CountDownLatch比较相似
CyclicBarrier在构造时给出线程数,只有等待的线程数到了构造方法中指定的数量,当最后一个线程等待后,所有的线程都会被释放,这个类是一个多线程汇合的工具。
 
Exchanger类,用exchange()方法可以使两个线程间相互交换对象,在两线程的同步点,等待第二个线程。在同步点时,交换对象,并同时被释放。
 
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值