java基础之 sun认证之3

来源:互联网 发布:js中给input赋值 编辑:程序博客网 时间:2024/04/30 06:59
第八章:高级语言特性
静态变量 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就是这种接口



原创粉丝点击