Core java note part02

来源:互联网 发布:营业执照制作软件下载 编辑:程序博客网 时间:2024/05/21 21:01

第八章:高级语言特性

静态变量 static

    一个类只有一个静态变量,跟对象没有关系。被类的所有实例共享;如果子类没有覆盖,也共享父类的静态成员。

    一般直接使用类名来访问 “类名.静态变量名”。可以在没有任何实例时调用。

    在某种意义上类似于全局变量(Java里没有全局变量,这只是C和C++的说法) 

    不能在 static 方法或代码块里访问非 static 成员(变量或方法) 

    能继承和覆盖,但覆盖 static 方法必须也是 static 的方法。


    1.可以修饰属性、方法、初始代码块,成为类变量、静态方法、静态初始化代码块。

       注:初始代码块是在类中而不是在任何方法之内的代码块。

    2.类变量、静态方法、静态初始化代码块与具体的某个对象无关,只与类相关,是全类公有的。 在类加载时初始化。

    3.类加载:JVM通过CLASSPATH找到字节码文件,并将字节码文件中的内容通过I/O流读到JVM并保存的过程

       在虚拟机的生命周期中一个类只被加载一次。

       注:Java命令的作用是启动JVM (Java Virtual Mechine)。

    4.tatic 定义的是一块为整个类共有的一块存储区域,其发生变化时访问到的数据都是经过变化的。

    5.为什么主方法必须是静态的?

       主方法是整个应用程序的入口,JVM只能通过类名去调用主方法。

    6.类变量和静态方法可以在没有对象的情况下用:类名.方法名(或属性名)来访问。

    7.静态方法不可被覆盖(允许在子类中定义同名的静态方法,但是没有多态)

       父类如果是静态方法,子类不能覆盖为非静态方法。父类如果是非静态方法,子类不能覆盖为静态方法。

       争论:静态方法可以覆盖但是没有多态。

       思考:没有多态的覆盖叫覆盖吗?

       在静态方法中不允许调用本类中的非静态成员。

    8.静态初始化代码块只在类加载的时候运行一次,以再也不执行了。所以静态代码块一般被用来初始化静态成员。

    9.不加static为动态初始化代码块,在创建对象时被调用(在构造函数之前)。

    10.最后要注意的一点就是 static 不能修饰局部变量。


什么时候类加载

    第一次需要使用类信息时加载。

    类加载的原则:延迟加载,能不加载就不加载。


触发类加载的几种情况:

    (1)、调用静态成员时,会加载静态成员真正所在的类及其父类。

         通过子类调用父类的静态成员时,只会加载父类而不会加载子类。

    (2)、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。 

    (3)、加载子类会先加载父类。

        注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。

           例:public static final int a =123;

        但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。

           例:public static final int a = math.PI

        如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载;

        如果编译时不能确定其值的话,则运行时加载



类加载的顺序:

 1.加载静态成员/代码块:

   先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。

   同一个类里的静态成员/代码块,按写代码的顺序加载。

   如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。

   调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。

 2.加载非静态成员/代码块:

   先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。

   同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。

   但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。

   调用父类的非静态成员(private 除外),也可以像调用自己的一样。

 3.调用构造方法:

   先递归地调用父类的构造方法(Object的最先);默认调用父类空参的,也可在第一行写明调用父类某个带参的。

   再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。

 注意:如果加载时遇到 override 的成员,可看作是所需创建的类型赋值给当前类型。

   其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。

   假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。

   由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;

   由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。


成员,包括变量和方法

成员变量,包括实例变量和静态变量

    


final 修饰符

    (最终的、最后的)当final修饰时,不能被改变,不能被继承

    1.final 可以用来修饰类、属性和方法、局部变量。

    2.final 修饰一个属性时,该属性成为常量。

     (1)对于在构造方法中利用final进行赋值时,此时在构造之前系统设置的默认值相对于构造方法失效。

     (2)对于实例常量的赋值有两次机会

         在初始化的时候通过声明赋值

         在构造的时候(构造方法里)赋值

         注:不能在声明时赋值一次,在构造时再赋值一次。

         注意:当final修饰实例变量时,实例变量不会自动初始化为0;但必须给他赋值才能通过编译。

    3.final 修饰方法时,该方法成为一个不可覆盖的方法。这样可以保持方法的稳定性。

       如果一个方法前有修饰词private或static,则系统会自动在前面加上final。

       即 private 和 static 方法默认均为 final 方法。

    4.final 常常和 static、public 配合来修饰一个实例变量,表示为一个全类公有的公开静态常量。

       例: public static final int a = 33;

       在这种情况下属性虽然公开了,但由于是一个静态常量所以并不算破坏类的封装。

    5.final 修饰类时,此类不可被继承,即final类没有子类。

       一个 final 类中的所有方法默认全是final方法。

       final 不能修饰构造方法,构造方法不能被继承更谈不上被子类方法覆盖。


关于 final 的设计模式:不变模式

    1、不变模式:一个对象一旦产生就不可能再修改( string 就是典型的不变模式);

         通过不变模式可以做到对象共享;

    2、池化思想:用一个存储区域来存放一些公用资源以减少存储空间的开销。

         有池的类型:boolean,byte,int,short,long,char,(池范围在-127~128之间)

         (float,double 等小数没有池) 

         例:在String类中有个串池(在代码区)。

         池:堆里的一片独立空间。目的是拿空间换时间,让运算效率更高。

     (1)如果用Stirng str = “abc” 来创建一个对象时,则系统会先在“串池”中寻找有没有“abc”这个字符串

         如果有则直接将对象指向串池中对应的地址,如果没有则在串池中创建一个“abc”字符串。

         所以:String str1 = “abc”;

         String str2 = “abc”;

         Str1 == str2 返回值是ture;他们的地址是一样的。

           也就是说str1和str2都指向了代码空间中相同的一个地址,而这个地址空间保存就是是字符串"abc"

         字符串是不可改变的类型,所以可以共享。所以串池里不会有相同的两个字符串。


     (2)如果用String str = new String("abc")则直接开辟一块内存放"abc"这个字符串。

         所以上面这语句,创建两个"abc",一个在池,一个是对象

         String str2 = new String("abc");

         Str == str2 返回值是false;他们的地址是不一样的。

         即是说str和str2分别指向了堆空间中不同的两个地址,而这两个地址空间保存的都是字符串"abc"


StringBuffer 类(java.lang下的)。

    对于字符串连接

     String str=”1”+”2”+”3”+”4”;

         产生:

          12    123    1234

    这在串池中产生多余对象,而我们真正需要的只有最后一个对象,这种方式在时间和空间上都造成相当大的浪费。

    所以我们应该使用 StringBuffer(线程安全的) 或者 StringBuilder(线程不安全的)来解决

         解决方案:    

      StringBuffer sb = new StringBuffer(“1”);

      Sb.append(“2”);

      Sb.append(“3”);

      Sb.append(“4”);

      S = sb.toString();

    解决后的方案比解决前在运行的时间上快2个数量级。

StringBuilder (1.5版本后出现的)

    线程不安全的,在多线程并发时会出现问题。但仍是字符串合并的首选。

    运行效率比 StringBuffer 快一倍。



abstract 抽象

    1.可用来修饰类、方法

    2.abstract 修饰类时,则该类成为一个抽象类。

       抽象类不可生成对象(但可以有构造方法留给子类使用),必须被继承使用。

       抽象类可以声明,作为编译时类型,但不能作为运行时类型。

       abstract 永远不会和 private,static,final 同时出现。( 因为抽象必须被继承。)

    3.abstract 修饰方法时,则该方法成为一个抽象方法,抽象方法不能有实现;由子类覆盖后实现。

       比较:private void print(){};表示方法的空实现

       abstract void print();表示方法为抽象方法,没有实现

    4.抽象方法从某中意义上来说是制定了一个标准,父类并不实现,留给子类去实现。

       注:抽象类中不一定要有抽象方法,但有抽象方法的类一定是抽象类。

       抽象类可以有抽象方法和非抽象方法。实现抽象类的第一个具体类必须实现其所有抽象方法。

    5.关于抽象类的设计模式:模板方法

        灵活性和不变性


interface 接口

    1、定义:接口不是类,而是一组对类需求的描述,这些类要遵从接口描述的统一格式进行定义。

       定义一个接口用关键字 interface。

       例:public interface a{……}

    2、接口是一种特殊的抽象类。

       在一个接口中,所有的方法为公开、抽象的方法,所有的属性都是公开、静态、常量。

       所以接口中的所有属性可省略修饰符:public static final。也只能用这三个修饰符。

       接口中所有的方法可省略修饰符:public abstract。但这些都是默认存在的。

    3、一个类实现一个接口必须实现接口中所有的方法,否则其为一抽象类。而且实现类的方法需要 public

       实现接口用关键字 implements. 

       所谓实现一个接口就是实现接口中所有的方法。

       例:class Aimple implements A{……..};

    4、一个类除了继承另一个类外(且只能继承一个类),还可以实现多个接口(接口之间用逗号分割)。

       接口可以实现变相的多继承。

       例:class Aimple extends Arrylist implements A,B,C{…}

    5、不能用“new 接口名”来实例化一个接口,但可以声明一个接口。

    6、接口与接口之间可以多继承。

       例:interface face1 extends face2,face3{}

       接口的继承相当于接口的合并

    7、接口的作用

     (1)、间接实现多继承。

           用接口来实现多继承并不会增加类关系的复杂度。因为接口不是类,是在类的基础上的再次抽象。

           父类:主类型     接口:副类型

           典例:父亲(主)  和  老师(副)

     (2)、允许我们为一个类定义出混合类型。

     (3)、通过接口制定标准

            接      口:标准的定义  定义标准

            接口的调用者:标准的使用  使用标准

            接口的实现类:标准的实现  实现标准

        接口的回调:先有接口的使用者,再有接口的实现者,最后把接口的实现者的对象传到接口的使用者中,

                 并由接口的使用者通过接口来调用接口实现者的方法。

        例:sun公司提供一套访问数据库的接口(标准),java程序员访问数据库时针对数据库接口编程。

           接口由各个数据库厂商负责实现。

     (4)、解耦合作用:采用接口可以最大限度的做到弱耦合,将标准的实现者与标准的制定者隔离

         (例:我们通过JDBC接口来屏蔽底层数据库的差异)

    8、接口的编程设计原则

     (1)、尽量针对接口编程(能用接口就尽量用接口)

     (2)、接口隔离原则(用若干个小接口取代一个大接口)

           这样可以只暴露想暴露的方法,实现一个更高层次的封装。

    9、注意点:

     (1)、一个文件只能有一个 public 接口,且与文件名相同。

     (2)、在一个文件中不能同时定义一个 public 接口和一个 public 类。

     (3)、接口与实体类之间只有实现关系,没有继承关系;

          抽象类与类之间只有继承关系没有实现关系。接口与接口之间只有继承关系,且允许多继承。

     (4)、接口中可以不写 public,但在子类实现接口的过程中 public 不可省略。


接口 VS 抽象类

    1、接口中不能有具体的实现,但抽象类可以。

    2、一个类要实现一个接口必须实现其里面所有的方法,而抽象类不必。

    3、通过接口可以实现多继承,而抽象类做不到。

    4、接口不能有构造方法,而抽象类可以。

    5、实体类与接口之间只有实现关系,而实体类与抽象类只有继承关系

      抽象类与接口之间既有实现关系又有继承关系。

    6、接口中的方法默认都是公开抽象方法,属性默认都是公开静态常量,而抽象类不是。


修饰符的使用情况:

(Y表可用;不写表示不可用) 

    修饰符         类       属性        方法      局部变量(所有局部变量都不能用修饰符)

    public        Y         Y          Y         

    protected               Y          Y                                     

    (default)     Y         Y          Y                      

    private                 Y          Y        

    static                  Y          Y                           

    final         Y         Y          Y         Y

    abstract      Y                    Y


访问权限控制:

    修饰符      同一个类    同一个包  (不同包)子类  其他包

    public        Y         Y          Y       Y

    protected     Y         Y          Y   

    (default)     Y         Y            

    private       Y       



Object类

   1、object类是类层次结构的根类,他是所有类默认的父类。

   2、object类中的其中三个方法。

   (1)、finalize()

        当一个对象被垃圾收集的时候,最后会由JVM调用这个对象的finalize方法;

        注意:这个方法一般不用,也不能将释放资源的代码放在这个方法里;只有调用C程序时,才可能要用到


   (2)、toString()

        存放对象地址的哈希码值。

        返回一个对象的字符串表示形式。打印一个对象其实就是打印这个对象toString方法的返回值。

        可以覆盖类的toString()方法,从而打印我们需要的数据。 Public String toString(){……}


    (3)、equals(Object obj)

        用来判断对象的值是否相等。前提是覆盖了equals方法。Object类中的equals方法判断的依然是地址

        注意:String类已经覆盖了equals方法,所以能用equals来判断String对象的值是否相等。

下面是覆盖equals方法的标准流程:

/*************************************************************************/

  public boolean equals(Object obj){

    //第一步:现判断两个对象地址是否相等

    if(this == obj)  return   true;

    //第二步:如果参数是null的话直接返回false;

    if(obj == null)  return   false;

    //第三步:如果两个对象不是同一个类型直接返回false

    if (getClass() != obj.getClass()) return false;

    //?? if(!(this.getClass.getName().equals(o.getClass.getName())) return false;

    //第四步:将待比较对象强转成指定类型,然后自定义比较规则

       Student s = (Student) obj;

    if(s.name.equals(this.name)&&s.age==this.age) return true;

    else return false;

}

/*************************************************************************/

         覆盖equals的原则: 1.自反性(自己=自己)、 2.对称性(y=x则x=y)、

                          3.一致性(多次调用,结果一致)、 4.传递性(A=B,B=C则A=C)。

         非空原则: t1.equals(Null)返回False;(如果t1不等于空) 


    (4)、clone 克隆,拷贝

        一个对象参与序列化过程,那么对象流会记录该对象的状态,当再次序列化时,

        会重复序列化前面记录的对象初始状态,我们可以用对象克隆技术来解决这个问题

         1 类中覆盖父类的clone方法,提升protected为public

         2 类实现Cloneable接口

        浅拷贝:只简单复制对象的属性

        深拷贝:如果被复制对象的属性也是一个对象,则还会复制这个属性对象

               这种复制是一个递归的过程,通过对象序列化实现


 



《Java5.0新特性》

 四大点(枚举、泛型、注释、..);5 小点(包装类、静态应用、可变长参数、for-each、..) 

一、自动装箱 和 自动解箱技术

    装箱Autoboxing,也翻译作 封箱;解箱Unautoboxing(也译作 解封)

    1、自动装箱技术:编译器会自动将简单类型转换成封装类型。

    2、编译器会自动将封装类型转换成简单类型

    3、注意:自动装箱和自动解箱只会在必要的情况下执行。

       int 能隐式提升成 long;但Integer不能隐式提升成Long,只能提升成Number

       封装之后就成类,只能由子类转成父类;而Integer和Long是Number的不同子类。

       如: int i; short s; Integer II; Short SS;

         可以 i=SS;   但不可以 II=s; //赋值时,右边的数先转成左边数的对应类型,再进行隐式类型提升



二、静态引用概念:

    用 import static 节省以后的书写。

        引入静态属性 import static java.lang.System.out;

        引入静态方法 import static java.lang.Math.random;

        import static 只能引入静态的方法或属性;不能只引入类或非静态的方法。

    如:import static java.lang.System.*;

     out.println(“a”);  //等于System.out.println("a"); 由于out是一个字段,所以不能更节省了

     如果 import static java.lang.System.gc; 则可以直接在程序中用 gc(); //等于System.gc();



三、可变长参数

    一个方法的参数列表中最多只能有一个可变长参数,而且这个变长参数必须是最后一个参数

    方法调用时只在必要时去匹配变长参数。

/**********变长参数的例子*************************************/

import static java.lang.System.*;//节省书写,System.out直接写out

public class TestVararg {

   public static void main(String... args){

      m();

      m("Liucy");

      m("Liucy","Hiloo");

    }

   static void m(String... s){out.println("m(String...)");}

   //s可以看作是一个字符串数组String[] s

   static void m(){out.println("m()");}

   static void m(String s){out.println("m(String)");}

}  //m(String... s) 是最后匹配的

/**********************************************************/



四、枚举 enum

    1、定义:枚举是一个具有特定值的类型,对用户来说只能任取其一。

       对于面向对象来说时一个类的对象已经创建好,用户不能新生枚举对象,只能选择一个已经生成的对象。

    2、枚举本质上也是一个类。枚举值之间用逗号分开,以分号结束(如果后面没有其他语句,分号可不写)。

    3、枚举分为两种:类型安全的枚举模式和类型不安全的枚举模式

    4、枚举的超类(父类)是:Java.lang.Enum。枚举是 final 类所以不能继承或被继承。但可以实现接口。

       枚举中可以写构造方法,但构造方法必需是私有的,而且默认也是 私有的 private

    5、一个枚举值实际上是一个公开静态的常量,也是这个类的一个对象。

    6、枚举中可以定义抽象方法,但实现在各个枚举值中(匿名内部类的方式隐含继承)

       由于枚举默认是 final 型,不能被继承,所以不能直接用抽象方法(抽象方法必须被继承)

       在枚举中定义抽象方法后,需要在自己的每个枚举值中实现抽象方法。

        

    枚举是编译期语法,编译后生成类型安全的普通类

    values()静态方法,返回枚举的元素数组

    name方法


/**********************************************************/

final class Season1{    //用 final 不让人继承

   private Season1(){}  //用 private 构造方法,不让人 new 出来

   public static final Season1 SPRING=new Season1("春");

   public static final Season1 SUMMER=new Season1("夏");

   public static final Season1 AUTUMN=new Season1("秋");

   public static final Season1 WINTER=new Season1("冬");

   String name; //将"春夏秋冬"设为本类型,而不是24种基本类型,为防止值被更改

   private Season1(String name){

      this.name=name;

    }

   public String getName(){

      return this.name;

}}

/********上面是以前版本时自定义的枚举,下面是新版的枚举写法********/

enum Season2{

   SPRING("春"), SUMMER("夏"),  AUTUMN("秋"),  WINTER("冬");

   String name;

   Season2(String name){ this.name=name; }

   public String getName(){return this.name;}

}//注意:枚举类是有序的;如:Season2.SPRING.ordinal()

/**********************************************************/

/*******关于枚举的例子****************************************/

import static java.lang.System.*;

public class TestTeacher {

    public static void main(String[] args) {

        for(TarenaTeacher t:TarenaTeacher.values()){

            t.teach();

}}}

enum TarenaTeacher{

    LIUCY("liuchunyang"){void teach(){out.println(name+" teach UC");}},

    CHENZQ("chenzongquan"){void teach(){out.println(name+" teach C++");}},

    HAIGE("wanghaige"){void teach(){out.println(name+" teach OOAD");}};

    String name;

    TarenaTeacher(String name){this.name=name;}

    abstract void teach();

}

/**********************************************************/

enum Animals {

    DOG ("WangWang") ,  CAT("meow") ,  FISH("burble");

    String  sound;

    Animals ( String s ) { sound = s; }

}

    class TestEnum {

    static  Animals  a;

        public static void main ( String[] args ) {

            System.out.println ( a.DOG.sound + " " + a.FISH.sound );

}}

/**********************************************************/


五、新型 for 循环 for—each,用于追求数组与集合的遍历方式统一

    1、数组举例:

      int[]  ss  =  {1,2,3,4,5,6};

      for(int i=0; i<ss.length; i++){

         System.out.print(ss[i]);

      }  //以上是以前的 for 循环遍历,比较下面的for—each

      System.out.println();

      for(int i : ss){

          System.out.print(i);

    2、集合举例:

       List  ll  =  new ArrayList();

       for(Object  o : ll ){

          System.out.println(o);

        }

    注:凡是实现了java.lang.Iterable接口的类就能用 for—each遍历

    用 for—each时,不能用list.remove()删除,因为他内部的迭代器无法调用,造成多线程出错。

    这时只能用 for 配合迭代器使用。


六、泛型 Generic 

    1、为了解决类型安全的集合问题引入了泛型。

       泛型是编译检查时的依据,也是编译期语法。

      (编译期语法:编译期有效,编译后擦除,不存在于运行期)

    2、简单的范型应用:集合(ArrayList, Set, Map, Iterator, Comparable) 

       List<String>  l  =  new  ArrayList<String>();

       <String>:表示该集合中只能存放String类型对象。

    3、使用了泛型技术的集合在编译时会有类型检查,不再需要强制类型转换。

       String str  =  l.get(2);  //因为List<String>  l, 所以 Error

       注:一个集合所允许的类型就是这个泛型的类型或这个泛型的子类型。

    4、List<Number>  l  =  new  ArrayList<Integer>  //Error

       List<Integer> l = new ArrayList<Integer>  //Right

        必须类型一致,泛型没有多态

    5、泛型的通配符<?>

       泛型的通配符表示该集合可以存放任意类型的对象。但只有访问,不可以修改。

        static void print( Cllection<?> c ){

         for( Object o : c )

         out.println(o);

        }

    6、带范围的泛型通配符

      泛型的声明约定T表示类型,E表示元素

        (1)、上界通配符,向下匹配:<?  extends  Number>    表明“extends”或“implements”,认为是 final 的

             表示该集合元素可以为Number类型及其子类型(包括接口),例如 Number,Integer,Double

             此时集合可以进行访问但不能修改。即不允许调用此对象的add,set等方法;但可以使用 for-each 或 get.

        (2)、下界通配符,向上匹配:<?  super  Number>

             表示该集合元素可以为Number类型及其父类型,直至 Object。

             可以使用 for-each,add,addAll,set,get等方法

        (3)、接口实现:<? extends Comparable>

             表示该集合元素可以为实现了Comparable接口的类

    7、泛型方法

        在返回类型与修饰符之间可以定义一个泛型方法,令后面的泛型统一

        这里只能用 extends 定义,不能用 super ;后面可以跟类(但只能有一个,且要放在首位)其余是接口

        符号只有   &    //“&”表示“与”;逗号表示后面的另一部分

        静态方法里面,不能使用类定义的泛型,只能用自己定义的;因为静态方法可以直接调用;

        所以普通方法可以使用类定义的及自己定义的泛型

         public static <T> void copy(T[] array,Stack<T> sta){……}

         public static <T,E extends T> void copy (T[] array,Stack<E> sta){…..}

         public static <T extends Number&Comparable> void copy(List<T> list,T[] t);

    8、不能使用泛型的情况:

      (1)、带泛型的类不能成为 Throwable 类和 Exception 类的子类

            因为cathc()中不能出现泛型。

      (2)、不能用泛型来 new 一个对象

            如:T t = new T();

      (3)、静态方法不能使用类的泛型,因为静态方法中没有对象的概念。

    9、在使用接口的时候指明泛型。

     class Student  implements  Comparable<Student>{…….}

     

    10、泛型类

    /********************************************************************/

   class MyClass<T>{

      public void m1(T t){}

      public T m2(){

        return null;

   }}

    /********************************************************************/


 



第九章:

内部类(nested classes) (非重点)

    1.定义:定义在其他类中的类,叫内部类(内置类)。内部类是一种编译时的语法,编译后生成

       的两个类是独立的两个类。内部类配合接口使用,来强制做到弱耦合(局部内部类,或私有成员内部类)。

    2.内部类存在的意义在于可以自由的访问外部类的任何成员(包括私有成员),但外部类不能直接访问内部类的

       成员。所有使用内部类的地方都可以不使用内部类;使用内部类可以使程序更加的简洁(但牺牲可读性),

       便于命名规范和划分层次结构。

    3.内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。

    4.内部类可用 static,protected 和 private 修饰。(而外部类只能使用 public 和 default)。

    5.内部类的分类:成员内部类、局部内部类、静态内部类、匿名内部类。

          (注意:前三种内部类与变量类似,可以对照参考变量)

        ① 成员内部类(实例内部类):作为外部类的一个成员存在,与外部类的属性、方法并列。

            成员内部类可看作外部类的实例变量。

            在内部类中访问实例变量:this.属性

            在内部类访问外部类的实例变量:外部类名.this.属性。

            对于一个名为outer 的外部类和其内部定义的名为inner 的内部类。

                编译完成后出现outer.class 和outer$inner.class 两类。

            不可以有静态属性和方法(final 的除外),因为 static 在加载类的时候创建,这时内部类还没被创建

            如果在外部类的外部访问内部类,使用out.inner.***

        建立内部类对象时应注意:

            在创建成员内部类的实例时,外部类的实例必须存在:

            在外部类的内部可以直接使用inner s=new inner();  因为外部类知道 inner 是哪个类。

            而在外部类的外部,要生成一个内部类对象,需要通过外部类对象生成。

                Outer.Inner in = new Outer().new Inner();

                相当于:Outer out = new Outer(); Outer.Inner in = out.new Inner();

            错误的定义方式:Outer.Inner in=new Outer.Inner()。

        ② 局部内部类:在方法中定义的内部类称为局部内部类。

            类似局部变量,不可加修饰符 public、protected 和 private,其范围为定义它的代码块。

            可以访问外部类的所有成员,此外,还可以访问所在方法中的 final 类型的参数和变量。

            在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。

            要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。

            局部内部类不能声明接口和枚举。

        ③ 静态内部类:(也叫嵌套类)

            静态内部类定义在类中,在任何方法外,用 static 定义。

            静态内部类能直接访问外部类的静态成员;

            不能直接访问外部类的实例成员,但可通过外部类的实例(new 对象)来访问。

            静态内部类里面可以定义静态成员(其他内部类不可以)。

            生成(new)一个静态内部类不需要外部类成员,这是静态内部类和成员内部类的区别。

                静态内部类的对象可以直接生成: Outer.Inner in=new Outer.Inner();

                对比成员内部类:Outer.Inner in = Outer.new Inner();

                而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。

            静态内部类不可用 private 来进行定义。例子:

对于两个类,拥有相同的方法:

/*************************************************************************/

   /*class People{void run();}

   interface Machine{void 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();

}}

/*************************************************************************/

            注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。

            这是唯一一种必须使用内部类的情况。

            用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。

        ④ 匿名内部类:

             【1】匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。

             【2】不同的是他是用一种隐含的方式实现一个接口或继承一个类,而且他只需要一个对象

             【3】在继承这个类时,根本就没有打算添加任何方法。

             【4】匿名内部类大部分情况都是为了实现接口的回调。

        注:匿名内部类一定是在 new 的后面

           其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。

            因其为局部内部类,那么局部内部类的所有限制都对其生效。

            匿名内部类是唯一一种无构造方法类。

            注:这是因为构造器的名字必须和类名相同,而匿名内部类没有类名。

            匿名内部类在编译的时候由系统自动起名Out$1.class。

            因匿名内部类无构造方法,所以其使用范围非常的有限。

            结尾需加上分号。


匿名内部类的例子:

    /*************************************************************************/

public class test{

  public static void main(String[] args){

     B.print(new A(){

    public void getConnection(){ System.out.println("Connection....");}

          });

}}


interface A{ void getConnection();}

class B{

   public static void print(A a){ a.getConnection();}

}

    /*************************************************************************/


    枚举和接口可以在类的内部定义,但不能在方法内部定义。

    接口里面还可以定义多重接口和类。

    类放在什么位置,就相当于什么成员。


内部类的用途:

    封装类型:把标准公开,把标准的实现者作为内部类隐藏起来,

            强制要求使用者通过标准访问标准的实现者,从而强制做到弱耦合!

    直接访问外部类的成员

    配合接口,实现多继承,当父类和接口方法定义发生冲突的时候,就必须借助内部类来区分

    模板回调

    

从内部类继承:

    由于直接构造实例内部类时,JVM会自动使内部类实例引用它的外部类实例。

    但如果下面Sample类通过以下方式构造对象时:Sample s = new Sample();

    JVM无法决定Sample实例引用哪个Outer实例,为了避免这种错误的发生,在编译阶段,java编译器会要求Sample类的构造方法必须通过参数传递一个Outer实例的引用,然后在构造方法中调用super语句来建立Sample实例与Outer实例的关联关系。

/*************************************************************************/

public class Sample extends Outer.Inner{

  //public Sample(){} //编译错误

  public Sample(Outer o){  o.super();  }

  public static void main(String args[]){

     Outer outer1=new Outer(1);

     Outer outer2=new Outer(2);

     Outer.Inner in=outer1.new Inner();

     in.print();

     Sample s1=new Sample(outer1);

     Sample s2=new Sample(outer2);

     s1.print();  //打印a=1

     s2.print();  //打印a=2

}}

/*************************************************************************/

  

内部接口:

    在一个类中也可以定义内部接口

    在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。

    在接口中还可以定义接口,这种接口默认也是public static 的,如Map.Entry就是这种接口



第八章 Collection FrameWork

《集合框架》

     ((Iterator接口  ←-  Iterable接口 )) ← Collection接口

                                                 ↑                 

          ┌--------------------------------┬---------------┐    

       Set接口                           List接口         Queue接口

          ↑                               ↑                ↑      

     ┌----------┐            ┌-----------+---------┐ ┌-----------┐           

  HashSet   SortedSet接口    Vector    ArrayList   LinkedList   PriorityQueue

                 ↑ 

               TreeSet

          

     Map接口

       ↑

     ┌----------┐ 

   HashMap  SortedMap接口

                 ↑ 

              TreeMap


各接口的主要方法:

   Iterable:     +iterator() 

   Iterator:     +hasNext()    +next()        +remove() 

   Collection:   +add()        +remove()      +clear()         +isEmpty() +size()  +contains() 

   List:         +get()        +set()         +remove() 

   Queue:        +element()    +offer()       +peek()          +poll() 

   Set:

     SortedSet:  +comparator() +first()       +last()          +headSet() +tailSet() 

   Map:          +clear()      +containsKey() +containsValue() +get()     +keySet() 

                 +isEmpty()    +remove()      +put()会替换重复键 +size()    +values() 

     SortedMap:  +comparator() +firstKey()    +lastKey()       +headMap() +tailMap() 


一、集合(容器,持有对象):是一个用于管理其他多个对象的对象,且只能保存对象的引用,不是放对象。

   1、Collection:   集合中每一个元素为一个对象,这个接口将这些对象组织在一起,形成一维结构。

   2、List:         有序、可重复。

      ArrayList:    数组。查询快,增删慢。(List是链表)

      Vector:       线程安全,但效率很差(现实中基本不用)

   3、Set:          无序,且不可重复(不是意义上的重复)。(正好与List 对应)

      HashSet:      用 hashCode() 加 equals() 比较是否重复

      SortedSet:    会按照数字将元素排列,为“可排序集合”默认升序。

      TreeSet:      按二叉树排序(效率非常高); 按Comparable接口的 compareTo() 比较是否重复

   4、Map:          其中每一个元素都是一个键值对( Key-Value)。键不能重复。可有一个空键。

      SortedMap:    根据 key 值排序的 Map。

      HashMap:      用 hashCode() 加 equals() 比较是否重复

   5、Queue:         队列:先进先出。

      PriorityQueue:   优先队列:元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序


注意:在“集合框架”中,Map 和Collection 没有任何亲缘关系。 

    Map的典型应用是访问按关键字存储的值。它支持一系列集合操作,但操作的是键-值对,而不是独立的元素

    因此 Map 需要支持 get() 和 put() 的基本操作,而 Set 不需要。 


    

《常用集合列表》

'         存放元素   存放顺序      元素可否重复      遍历方式   排序方式       各自实现类

List      Object    有序         可              迭代       (2)      ArrayList, TreeSet

Set       Object    无序         不可             迭代      SortedSet    HashSet

SortedSet Object    无序         不可             迭代      已排序        TreeSet

Map       (1)       Key无序      Key不可,value可  对Key迭代  SortedMap    HashMap

SortedMap (1)       无序,有排序   Key不可,value可  对Key迭代  已对键值排序   TreeMap

     (1)Object(key)—Object(value); 

     (2)Collections.sort();


注:以上有序的意思是指输出的顺序与输入元素的顺序一致

    HashSet、HashMap通过hashCode(),equals()来判断重复元素

    在java中指定排序规则的方式只有两种:1、实现java.util包下的Comparator接口

                                  2、实现java.lang包下的Comparable接口


二、迭代器:Iterator

    1、使用Iterator接口方法,您可以从头至尾遍历集合,并安全的从底层Collection中除去元素

    2、remove() 由底层集合有选择的支持。底层集合支持并调用该方法时,最近一次next()返回的元素将被删

    3、Collection 接口的iterator() 方法返回一个Iterator

    4、Iterator中的hasNext()用于判断元素右边是否有数据,返回True则有。然后就可以调用next()动作。

    5、Iterator中的next()方法会将游标移到下一个元素,并返回它所跨过的元素。(通常这样遍历集合) 

    6、用于常规Collection 的Iterator 接口代码如下:

    /*迭代遍历*/

      List l = new ArrayList();

     Iterator it = l.iterator();

     while(it.hasNext()){

         Object obj = it.next();

         System.out.println(obj);

        }

注:工具类是指所有的方法都是公开静态方法的类。

    Java.util.collections就是一个工具类;


三、对集合的排序

    1、我们可以用Java.util.collections中的sort(List l)方法对指定的List集合进行排序;

    但是如果List中存放的是自定义对象时,这个方法就行不通了,必须实现Comparable接口并且指定排序规则。

        这里我们再来看一下sort(List l)方法的内部实现;

/**********************************************************/

class Collections2{

   public static void sort(List l){

      for(int i=0;i<l.size()-1;i++){

     for(int j=i+1;j<l.size();j++){

        Object o1 = l.get(i);

        Object o2 = l.get(j);

        Comparable c1 = (Comparable)o1;

        Comparable c2 = (Comparable)o2;

        if(c1.compareTo(c2)>0){

          Collections.swap(l,i,j);

}}}}}  //其实用的算法就是个冒泡排序。

/******************************************************/


    2、实现Java.lang.Comparable接口,其实就是实现他的 public int compareTo(Object obj)方法;

       比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

          其规则是当前对象与obj 对象进行比较,其返回一个 int 值,系统根据此值来进行排序。

          如当前对象 > obj,则返回值>0;

          如当前对象 = obj,则返回值=0;

          如当前对象 < obj,则返回值<0。


    注意:String类型已经实现了这个接口,所以可以直接排序;

    /******************************************************/

class Student implements Comparable{

   private String name;

      private int age;

      public Student(String name, int age) {

     this.name = name;

     this.age = age;

    }

      public int compareTo(Object obj) {

     Student s = (Student)obj;

     return s.age-this.age;

}}

    /******************************************************/


四、ArrayList和LinkedList集合

    1、ArrayList 底层是object 数组,所以ArrayList 具有数组的查询速度快的优点以及增删速度慢的缺点。

      Vector 底层实现也是数组,但他是一个线程安全的重量级组件。

    2、而在LinkedList 的底层是一种双向循环链表。

      在此链表上每一个数据节点都由三部分组成:

        前指针(指向前面的节点的位置)、 数据、 后指针(指向后面的节点的位置)。

      最后一个节点的后指针指向第一个节点的前指针,形成一个循环。

    3、双向循环链表的查询效率低但是增删效率高。所以LinkedList 具有查询效率低但增删效率高的特点。

    4、ArrayList 和LinkedList 在用法上没有区别,但是在功能上还是有区别的。

      LinkedList 经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。

      队列:先进先出的数据结构。

      堆栈:后进先出的数据结构。

     (堆栈就是一种只有增删没有查询的数据结构)

      注意:使用堆栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。

    

      LinkedList 提供以下方法:(ArrayList 无此类方法)

      addFirst(); +removeFirst(); +addLast(); +removeLast();

        在堆栈中,push 为入栈操作,pop 为出栈操作。

      Push 用addFirst();pop 用removeFirst(),实现后进先出。

        用isEmpty()--其父类的方法,来判断栈是否为空。

        在队列中,put 为入队列操作,get 为出队列操作。

      Put 用addFirst(),get 用removeLast()实现队列。

      List 接口的实现类 Vector 与ArrayList 相似,区别是Vector 是重量级组件,消耗的资源较多。

        结论:在考虑并发的情况下用Vector(保证线程的安全)。

        在不考虑并发的情况下用ArrayList(不能保证线程的安全)。

    

    5、面试经验(知识点):

      java.util.stack(stack 即为堆栈)的父类为Vector。可是stack 的父类是最不应该为Vector 的。

      因为Vector的底层是数组,且Vector有get方法(意味着它可能访问任意位置的元素,很不安全)。

      对于堆栈和队列只能用push 类和get 类。(这是早期的某个java编写工程师的失误造成) 

      Stack 类以后不要轻易使用。实现堆栈一定要用LinkedList。

        (在JAVA1.5 中,collection 有queue 来实现队列。) 


五、HashSet集合

   1、HashSet是无序的,没有下标这个概念。HashSet集合中元素不可重复(元素的内容不可重复);

   2、HashSet 底层用的也是数组。

   3、HashSet如何保证元素不重复?Hash算法和equals方法。

      当向数组中利用add(Object obj)添加对象的时候,系统先找对象的hashCode:

      int hc=obj.hashCode(); 返回的hashCode 为整数值。

      int I=hc%n;(n 为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n 为6 为例,

      如果I=0则放在数组a[0]位置,如果I=1则放在数组a[1]位置。

      如果equals()返回true,则说明数据重复。如果equals()返回false,则再找其他的位置进行比较。

      这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode 不同。

      如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。

        

      在实例中,定义student 对象时覆盖它的hashcode。

      因为String类会自动覆盖,所以比较String 类的对象时,不会出现相同的string 对象的情况。

      现在,在大部分的JDK 中,都已经要求覆盖了hashCode。 

    结论:如将自定义类用hashSet 来添加对象,一定要覆盖hashcode()和equals(),

      覆盖的原则是保证当两个对象hashcode 返回相同的整数,而且equals()返回值为True。

      如果偷懒,直接将hashCode方法的返回值设为常量;虽然结果相同,但会多次地调用equals(),影响效率。

      我们要保证相同对象的返回的hashCode 一定相同,也要保证不相同的对象的hashCode 尽可能不同

      (因为数组的边界性,hashCode 还是有极微几率相同的)。



六、TreeSet集合

    1、TreeSet是SortedSet的实现类TreeSet通过实现Comparable接口的compareTo来实现元素不重复。

    2、TreeSet由于每次插入元素时都会进行一次排序,因此效率不高。

    3、java.lang.ClassCastException是类型转换异常。

    4、在我们给一个类用CompareTo()实现排序规则时

    5、从集合中以有序的方式抽取元素时,可用TreeSet,添加到TreeSet的元素必须是可排序的。

      “集合框架”添加对Comparable 元素的支持。

      一般说来,先把元素添加到HashSet,再把集合转换为TreeSet 来进行有序遍历会更快。


七、Map

   1、HashMap集合

     (1)HashMap就是用hash算法来实现的Map

     (2)在实际开发中一般不会用自定义的类型作为Map的Key。做Key的无非是八中封装类。

     (3)HashMap的三组操作:

       【1】改变操作,允许从映射中添加和除去键-值对。键和值都可以为null。

           不能把Map作为一个键或值添加给自身。

         –Object put(Object key, Object value) 

         –Object remove(Object key) 

         –void clear() 

       【2】查询操作允许您检查映射内容:

         –Object get(Object key)

         –intsize()

         –boolean isEmpty()

       【3】最后一组方法允许您把键或值的组作为集合来处理。

         –public Set KeySet(); 

         –public Collection values() 

    (4)HashMap和HashTable的区别等同于ArrayList和Vector的区别。

       只不过HashTable中的Key和Value不能为空,而HashMap可以。

    (5)HashMap底层也是用数组,HashSet底层实际上也是HashMap,HashSet类中有HashMap属性(查API)。

       HashSet 实际上为(key.null)类型的HashMap。有key 值而没有value 值。


   2、HashMap 类和TreeMap 类

     ?集合框架提供两种常规Map 实现:HashMap和TreeMap。

     ?在Map 中插入、删除和定位元素,HashMap 是最好选择。

     ?如果要按顺序遍历键,那么TreeMap 会更好。

     ?根据集合大小,先把元素添加到HashMap,再把这种映射转换成一个用于有序键遍历的TreeMap 可能更快。

     ?使用HashMap 要求添加的键类明确定义了hashCode() 实现。

     ?有了TreeMap 实现,添加到映射的元素一定是可排序的

     ?HashMap和TreeMap 都实现Cloneable 接口。



异常 Exception

1.概念: JAVA将所有的错误封装成为一个对象,其根本父类为Throwable。异常处理可以提高我们系统的容错性。

  Throwable 有两个子类:Error 和Exception。


  Error:一般是底层的不可恢复的错误。

           Object 

             ↑ 

          Throwable 

             ↑ 

        ┌---------┐

     Error      Exception 

                   ↑                非RuntimeException

           ┌-----------------------┬---------------┐ 

        RuntimeException   InterruptedException  IOException

              ↑ 

        ┌----------------------┬-----------...

   NullpointerException  ArrayIndexOutOfBoundsException


2.Exception分类:

   Runtime exception(未检查异常)和 非Runtime exception(已检查异常)。

   未检查异常是因为程序员没有进行必要的检查,因为他的疏忽和错误而引起的异常。可避免。

    几个常见的未检查异常:

     ①java.lang.ArithmeticException           //如:分母为0; 

     ②java.lang.NullPointerException          //如:空指针操作; 

     ③java.lang.ArrayIndexoutofBoundsExceptio //如:数组越界;数组没有这元素;

     ④java.lang.ClassCastException            //如:类型转换异常; 

    已检查异常是不可避免的,对于已检查异常必须处理。


3、异常对象的传递。

   当一个方法中出现了异常而又没做任何处理,则这个方法会返回该异常对象。

   异常依次向上层调用者传递,直到传到JVM,虚拟机退出。  (用一句话说就是沿着方法调用链反向传递)

   应该在合适的位置处理异常,不让它一直上抛到 main 方法,应遵循的规则:

      谁知情谁处理,谁负责谁处理,谁导致谁处理。


4、如何来处理异常(这里主要是针对已检查异常)

    【1】throws 消极处理异常的方式。

        方法名(参数表)throws 后面接要往上层抛的异常。

        表示该方法对指定的异常不作任何处理,直接抛往上一层。

    【2】积极处理方式 try、catch

        try {可能出现错误的代码块} catch(exception e){进行处理的代码} ;

         一个异常捕获只会匹配一次 try,catch.

         一个异常一旦被捕获就不存在了。

        catch 中要求必须先捕获子类异常再捕获父类异常。 catch 要求有零到多个,但异常的名字不能重复。

    【3】finally (紧接在 catch 代码块后面)

        finally 的代码块是无论如何都会被执行的(除非虚拟机退出),所以里面一般写释放资源的代码。

        return 也无法阻止 finally,但System.exit(0):退出虚拟机则可以。

     

      有 finally 的 try/catch 流程

      如果 try 块失败了,抛出异常,程序马上转移到 catch 块,完成后执行 finally 块,再执行其后程序。

      如果 try 块成功,程序跳过 catch 块并去到 finally 块,finally 块完成后,继续执行其后程序。

      如果 try 或 catch 块有 return 语句,finally 还是会执行;程序会跳去执行 finally 块然后再回到 return 语句

/********** 使用try/catch的例子 ********************************************/

class MyException{

     void myException(){

        System.out.println("MyException");

//        if(1==1)return;   //测试没有异常时

        System.out.println(1/0);

    }     

    public static void main(String[] args) {

        MyException me = new MyException();

        try{

            System.out.println("try");

//            if(1==1)return;

            me.myException();

        }catch(ArithmeticException ae){

            System.out.println("cath");

        }finally{

            System.out.println("finally");

}}}

/*************************************************************************/


5、自定义异常(与一般异常的用法没有区别)

class MyException extends Exception{

   public MyException(String message){ super(message);}

   public MyException(){}


6、如何控制 try 的范围

   根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,

   那么这个我们就说这两个程序代码存在关联,应该放在同一个 try 中。


7、不允许子类比父类抛出更多的异常。



8、断言:只能用于代码调试时用。

  一般没什么用,只在测试程序时用

    /********* 断言的列子 ******************************************************/

public class TestAssertion {

   public static void main(String[] args){

      int i = Integer.parseInt(args[0]);

      assert i==1:"ABCDEFG";   //格式: assert (布尔表达式/布尔值) : String;

      /*断言语句:(表示断言该boolean语句返回值一定为真,如果断言结果为false就会报Error错误) 

        “ : ”后面跟出现断言错误时要打印的断言信息。 */

      System.out.println(i);

}}


//javac -source 1.4 TestAssertion.java     //表示用1.4的新特性来编译该程序。

//java -ea TestAssertion 0                 //表示运行时要用到断言工具

    /*************************************************************************/

0 0
原创粉丝点击