基础篇

    技术2022-05-11  72

    JAVA基础知识

           名词与修饰:Field字段、Method方法、Constructor构造、Parameter参数、Examples例子、componentType类型。

           基本9个类型(int、float、double、short、lang、boolean、byte、char、void)-- strictfp:关键字标记的方法或类必须使用严格的浮点运算.public static strictfp void main(String agrs[]);--常量final/类常数static final;一个类的声明为final,它的方法自动设置成final;--所有函数都是某个类的方法;

          <</>>位右移或位左移1<<35=1<<3=8;>>>对前面位填0;

      

    数据类型 存储要求 初始值 [标识] 检查 Int 4 byte 0 16进制前缀OX isJavaIdentifierStart和isJavaIdentifierPart检查是否是java字母 Short 2 8进制前缀0 Long 8 后缀L Byte 1   Char 1 Null 16Unicode:/u???? Obj       Double 8 0.0 后缀[D][null] Double.isNaN(value)非数字 Float 4 后缀F   Boolean 1 False   与数值无关

     

     

     

     

     

          类型:

    Xxx Int Short Long Float Double Char Byte [String]xxx* Int   (float)x (long)x (float)x (double)x (Char)x (Byte)x Integer.toString(x) Character.toString( x) 同此类方法   Short (int)x   Long (float)x   Float (long)x   Double (float)x   Char (double)x  

     

     

     

     

           对象和类概论:--对象转换:子—父类—子类,对象变量不包含对象只是一个指向一个对象,没有NEW当前类的对象大都是如此。--判断一个对象是否属于一个类:object instanceof Class。--对象引用是通过值传递的:           1.方法不能修改基本类型的参数。           2.方法可以修改对象参数的状态。           3.方法不能让对象参数指向新的对象。--一个方法的定义必须在内部定义包括main所有方法都时按值传递,数组和对象实际上就时引用(指针),方法可以改变对象状态. Java没有多维数组,多维数组就是数组的数组;数组拷贝:System.arraycopy(from.fromIndex,to,toIndex,count)--类与类之间关系:依赖,聚合,继承

    访问控制 private成员 缺省的成员 protected成员 public成员 同一类中可见 Ö Ö Ö Ö 同一包或子类中可见 × Ö Ö Ö 不同包中子类可见 × × Ö Ö 一切可见 × × × Ö

    Default同包可见,protected继承和同包可见

           垃圾回收:finalize()方法添加任何类中,在系统垃圾回收之前调用。System.runFinalizersOnExit(true)方法中确保finalize()方法在java关闭时调用;另一种替代的方法是使用Runtime.getRuntime().addShutdownHook()方法增加”关闭钩子”。 清除资源用dispose方法。       Examples:

    public class test

    {        static        {               System.out.print(“Hello World!”);   //打印,忽略异常               System.exit(0);        } } --构造重载中,在构造中调用其他构造 Constructor(String str,int x){…}; Constructor(String str){this(Str,2)};//调用上一个

           包 编译:

    --编译javac *.java;--执行java java_name;--打包javac –d pack_path *.java;--展开JAVA包jar xvf 包名[src.zip];--展开注释生成注释文档javadoc –d path *.java--两个java文件名相同,例如Employee.java,EmployeeTest.java编译时javac Employee*.java--jar文件。它是Java语言压缩工具。该工具可以将Java源程序打包成一个比原文件小的jar文件。--javadoc文件。javadoc文件是用于生成API文档。--javah文件。javah文件用于从Java类中调用C++语言代码。--javap文件。javap文件是用于分析字节码文件。

           概论与通用注释:import只能引入包,而不是对象。注释方法://…;/*…*/;/**…*/.@author name 作者@version text 版本@since text 始自@deprecated text 此类、方法、变量不应再被使用@see 参见--展开注释生成注释文档javadoc –d path *.java | package…

           Object of Methods  toStringpublic String toString()返回该对象的字符串表示。 toString 方法一般返回一个用文本表示这个对象的字符串。 给出的结果是简短明了的表示,这样便于人们读取。 建议任何子类都覆盖这个方法。 类 Object 的 toString 方法返回一个字符串、有符号 `@' 字符和该对象的无符号十六进制散列码表示,这里的字符串包括实例对象的类名。 返回值: 该对象的字符串表示。 重写toString()和valueOf()public static String valueOf(Object obj) {      return (obj == null) ? "null" : obj.toString(); }equals与”= =”public boolean equals(Object obj)比较两对象是否相等。 equals 方法实现一个等价关系: equals与”= =”= =是面向过程的操作符;equals是面向对象的操作符= =不属于任何类,equals则是任何类(在Java中)的一个方法;我们可以1)Primitive1 (基本类型)= = Primitive2(基本类型);2)Object Reference1(对象引用)= = Object Reference2(对象引用)3)Object Reference1 (对象引用) .equals(Object Reference2 (对象引用))如果要比较两个基本类型是否相等,请用= =;如果要比较两个对象引用是否相等,请用= =;欲比较栈中数据是否相等,请用= =; 欲比较堆中数据是否相等,请用equals;因为(根)基本类型,(根)对象引用都在栈中(所谓“根”,指未被任何其他对象所包含); 而对象本身在堆中。equals:比较指针地址中的值是否相同. 只适合与String,在其他对象的作用就等价于”= =”. “= =”:比较指针地址空间是否相同.

           重载技巧:1.判断参数对象是否属于一个类instanceof2.判断参数对象是否为null3.判断参数对象的字段值是否为null4.比较同类对象的字段值(supper.equals)   public boolean equals(Object obj)    {        if (obj = = null) return false;        if (!(obj instanceof FieldPosition))            return false;        FieldPosition other = (FieldPosition) obj;        if (attribute == null) {            if (other.attribute != null) {                return false;            }        }        else if (!attribute.equals(other.attribute)) {            return false;        }        return (beginIndex = = other.beginIndex            && endIndex = = other.endIndex            && field = = other.field);    } 

           Clone:protected native Object clone() throws CloneNotSupportedException创建与该对象的类相同的新对象。 然后初始化新建对象域,对应域指定为相同的值。 没有调用构造子。 当一个对象指示愿意复制它的实例时, Object 类的 clone 方法才复制该对象。 当一个类声明它实现 cloneable 接口时,能复制它的实例。 返回值: 该实例的复制。 抛出: CloneNotSupportedException 如果该对象类不支持 cloneable 接口。覆盖了 clone 方法的子类可能抛出此异常,表示不能复制实例。 抛出: OutOfMemoryError 如果没有足够内存。 重载技巧:分开对象基本类型,用set/get设置值.因为值不是对象.分开对象的子对象类型,set/get设置一个临时对象,然后克窿supper.clone返回这个对象什么是"clone"?在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。怎样应用clone()方法?class CloneClass implements Cloneable{    public int aInt;    public Object clone(){        CloneClass o = null;        try{            o = (CloneClass)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        return o;    }}有三个值得注意的地方,一是希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。另一个值得请注意的是重载了clone()方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。应该说第三点是最重要的,仔细观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。对于第二点,也要观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。那么clone类为什么还要实现Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。以上是clone的最基本的步骤,想要完成一个成功的clone,还要了解什么是"影子clone"和"深度clone"。影子clone下面的例子包含三个类UnCloneA,CloneB,CloneMain。CloneB类包含了一个UnCloneA的实例和一个int类型变量,并且重载clone()方法。CloneMain类初始化UnCloneA类的一个实例b1,然后调用clone()方法生成了一个b1的拷贝b2。最后考察一下b1和b2的输出: package clone;class UnCloneA {    private int i;    public UnCloneA(int ii) { i = ii; }    public void doubleValue() { i *= 2; }    public String toString() {        return Integer.toString(i);    }}class CloneB implements Cloneable{    public int aInt;    public UnCloneA unCA = new UnCloneA(111);    public Object clone(){        CloneB o = null;        try{            o = (CloneB)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        return o;    }}public class CloneMain {    public static void main(String[] a){        CloneB b1 = new CloneB();        b1.aInt = 11;        System.out.println("before clone,b1.aInt = "+ b1.aInt);        System.out.println("before clone,b1.unCA = "+ b1.unCA);                        CloneB b2 = (CloneB)b1.clone();        b2.aInt = 22;        b2.unCA.doubleValue();        System.out.println("=================================");        System.out.println("after clone,b1.aInt = "+ b1.aInt);        System.out.println("after clone,b1.unCA = "+ b1.unCA);        System.out.println("=================================");        System.out.println("after clone,b2.aInt = "+ b2.aInt);        System.out.println("after clone,b2.unCA = "+ b2.unCA);    }}  /** RUN RESULT:before clone,b1.aInt = 11before clone,b1.unCA = 111=================================after clone,b1.aInt = 11after clone,b1.unCA = 222=================================after clone,b2.aInt = 22after clone,b2.unCA = 222*/输出的结果说明int类型的变量aInt和UnCloneA的实例对象unCA的clone结果不一致,int类型是真正的被clone了,因为改变了b2中的aInt变量,对b1的aInt没有产生影响,也就是说,b2.aInt与b1.aInt已经占据了不同的内存空间,b2.aInt是b1.aInt的一个真正拷贝。相反,对b2.unCA的改变同时改变了b1.unCA,很明显,b2.unCA和b1.unCA是仅仅指向同一个对象的不同引用!从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。大多时候,这种clone的结果往往不是我们所希望的结果,这种clone也被称为"影子clone"。要想让b2.unCA指向与b2.unCA不同的对象,而且b2.unCA中还要包含b1.unCA中的信息作为初始信息,就要实现深度clone。深度clone把上面的例子改成深度clone很简单,需要两个改变:一是让UnCloneA类也实现和CloneB类一样的clone功能(实现Cloneable接口,重载clone()方法)。二是在CloneB的clone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();程序如下: package clone.ext;class UnCloneA implements Cloneable{    private int i;    public UnCloneA(int ii) { i = ii; }    public void doubleValue() { i *= 2; }    public String toString() {        return Integer.toString(i);    }    public Object clone(){        UnCloneA o = null;        try{            o = (UnCloneA)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        return o;    }}class CloneB implements Cloneable{    public int aInt;    public UnCloneA unCA = new UnCloneA(111);    public Object clone(){        CloneB o = null;        try{            o = (CloneB)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        o.unCA = (UnCloneA)unCA.clone();        return o;    }}public class CloneMain {    public static void main(String[] a){        CloneB b1 = new CloneB();        b1.aInt = 11;        System.out.println("before clone,b1.aInt = "+ b1.aInt);        System.out.println("before clone,b1.unCA = "+ b1.unCA);                        CloneB b2 = (CloneB)b1.clone();        b2.aInt = 22;        b2.unCA.doubleValue();        System.out.println("=================================");        System.out.println("after clone,b1.aInt = "+ b1.aInt);        System.out.println("after clone,b1.unCA = "+ b1.unCA);        System.out.println("=================================");        System.out.println("after clone,b2.aInt = "+ b2.aInt);        System.out.println("after clone,b2.unCA = "+ b2.unCA);    }} 

     

    /** RUN RESULT:before clone,b1.aInt = 11before clone,b1.unCA = 111=================================after clone,b1.aInt = 11after clone,b1.unCA = 111=================================after clone,b2.aInt = 22after clone,b2.unCA = 222*/可以看出,现在b2.unCA的改变对b1.unCA没有产生影响。此时b1.unCA与b2.unCA指向了两个不同的UnCloneA实例,而且在CloneB b2 = (CloneB)b1.clone();调用的那一刻b1和b2拥有相同的值,在这里,b1.i = b2.i = 11。要知道不是所有的类都能实现深度clone的。例如,如果把上面的CloneB类中的UnCloneA类型变量改成StringBuffer类型,看一下JDK API中关于StringBuffer的说明,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffer类型对象或和StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是SringBuffer对象,而且变量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原来的是:o.unCA = (UnCloneA)unCA.clone();还要知道的是除了基本数据类型能自动实现深度clone以外,String对象是一个例外,它clone后的表现好象也实现了深度clone,虽然这只是一个假象,但却大大方便了我们的编程。Clone中String和StringBuffer的区别应该说明的是,这里不是着重说明String和StringBuffer的区别,但从这个例子里也能看出String类的一些与众不同的地方。下面的例子中包括两个类,CloneC类包含一个String类型变量和一个StringBuffer类型变量,并且实现了clone()方法。在StrClone类中声明了CloneC类型变量c1,然后调用c1的clone()方法生成c1的拷贝c2,在对c2中的String和StringBuffer类型变量用相应的方法改动之后打印结果: package clone;class CloneC implements Cloneable{    public String str;    public StringBuffer strBuff;    public Object clone(){        CloneC o = null;        try{            o = (CloneC)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        return o;    }}public class StrClone {    public static void main(String[] a){        CloneC c1 = new CloneC();        c1.str = new String("initializeStr");        c1.strBuff = new StringBuffer("initializeStrBuff");        System.out.println("before clone,c1.str = "+ c1.str);        System.out.println("before clone,c1.strBuff = "+ c1.strBuff);          CloneC c2 = (CloneC)c1.clone();        c2.str = c2.str.substring(0,5);        c2.strBuff = c2.strBuff.append(" change strBuff clone");        System.out.println("=================================");        System.out.println("after clone,c1.str = "+ c1.str);        System.out.println("after clone,c1.strBuff = "+ c1.strBuff);        System.out.println("=================================");        System.out.println("after clone,c2.str = "+ c2.str);        System.out.println("after clone,c2.strBuff = "+ c2.strBuff);    }}  /* RUN RESULTbefore clone,c1.str = initializeStrbefore clone,c1.strBuff = initializeStrBuff=================================after clone,c1.str = initializeStrafter clone,c1.strBuff = initializeStrBuff change strBuff clone=================================after clone,c2.str = initiafter clone,c2.strBuff = initializeStrBuff change strBuff clone**/打印的结果可以看出,String类型的变量好象已经实现了深度clone,因为对c2.str的改动并没有影响到c1.str!难道Java把Sring类看成了基本数据类型?其实不然,这里有一个小小的把戏,秘密就在于c2.str = c2.str.substring(0,5)这一语句!实质上,在clone的时候c1.str与c2.str仍然是引用,而且都指向了同一个String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的String类型,然后又赋回给c2.str。这是因为String被Sun公司的工程师写成了一个不可更改的类(immutable class),在所有String类中的函数都不能更改自身的值。下面给出很简单的一个例子:package clone; public class StrTest { public static void main(String[] args) { String str1 = "This is a test for immutable"; String str2 = str1.substring(0,8); System.out.println("print str1 : " + str1);System.out.println("print str2 : " + str2); } } /* RUN RESULT print str1 : This is a test for immutable print str2 : This is */例子中,虽然str1调用了substring()方法,但str1的值并没有改变。类似的,String类中的其它方法也是如此。当然如果我们把最上面的例子中的这两条语句c2.str = c2.str.substring(0,5);c2.strBuff = c2.strBuff.append(" change strBuff clone");改成下面这样:c2.str.substring(0,5);c2.strBuff.append(" change strBuff clone");去掉了重新赋值的过程,c2.str也就不能有变化了,我们的把戏也就露馅了。但在编程过程中只调用c2.str.substring(0,5);语句是没有任何意义的。应该知道的是在Java中所有的基本数据类型都有一个相对应的类,象Integer类对应int类型,Double类对应double类型等等,这些类也与String类相同,都是不可以改变的类。也就是说,这些的类中的所有方法都是不能改变其自身的值的。这也让我们在编clone类的时候有了一个更多的选择。同时我们也可以把自己的类编成不可更改的类。hashCodepublic native int hashCode()返回该对象的散列码值。 该方法支持散列表的优点,如 java.util.Hashtable 提供的散列表。 hashCode 的一般合同是: 不管一个 Java 应用程序调用它多少次, hashCode 方法始终返回同一个整数。 当同一应用程序从一个执行转到另一个执行时,该整数不必保持一致。 如果两个对象按照 equals 方法相等, 那么每个对象调入 hashCode 方法必须产生相同的整数结果。两个hashCode相等,但未必对象equals is true 返回值: 该对象的散列值。 重写 (1)如果两个String对象的内容相等,那么它们的hashCode()方法的返回值也相等。(2)如果两个String对象的内容不相等,那么它们的hashCode()方法的返回值则不一定不相等。查看String.java的源代码,可以看到hashCode的计算公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]代码如下:public int hashCode() {    int h = hash;    if (h == 0) {        int off = offset;        char val[] = value;        int len = count;            for (int i = 0; i < len; i++) {                h = 31*h + val[off++];            }            hash = h;        }        return h;    }用下面的代码进行实验,可以看到两个两个不同字符串返回相同的hashCode值。String s1 = new String("BB");String s2 = new String("Aa");System.out.println(s1.hashCode());System.out.println(s2.hashCode());  第一条:重写equals必须要重写hashCode!下面是java.lang.Object中的描述:1.在一个应用程序的执行期间,如果在同一个对象上多次调用hashCode方法,而使用equals方法作比较所用的信息又没有修改的话,那么该方法应该一直返回同一个int值。2.如果两个对象使用equals()方法比较相等的话,那么在这两个对象上面调用hashCode方法的返回值也应该相等。3.如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。第二条:相同的对象的hashCode必须相同!在Object. hashCode()方法中,同一个对象的两个不同实例,其hashCode是不同的,所以必须要重写。下面是一个好的处方:1.将一个非零的常数值,比如17,保存在名为result的int类型的变量中。2.对于对象中每一个关键字段(指equals方法中考虑的字段),完成下面的步骤:a)为该字段计算int类型的散列码:1)如果该字段是bloolean类型,则计算(f?0:1)2)如果该字段是byte,char,short或int类型,则计算(int)f3)如果该字段是long类型,则计算(int)(f^(>>>32))4)如果该字段是float类型,则计算Float.floatToIntBits(f)5)如果该字段是double类型,则计算Double.doubleToLongBits(f)得一long类型值,然后按前述计算此long类型的散列值6)如果该字段是一个对象引用,则利用此对象的hashCode,如果字段的值为null,则返回07)如果该字段是一个数组,则对每一个数组元素当作单独的字段来处理,然后安下一步的方案来进行合成2)利用下面的公式将散列码c 组合到result中。result=37*result+c。3)返回result。4)完成hashCode方法后,问自己是否相同的实例拥有相同的hash码,如果答案为否,找出问题所在并解决。如果一个类是不可变的,而且计算散列码的代价比较高,那么可以考虑在对象内部缓存散列码,而不是在每次请求的时候都重新计算。如果你确定对象内部的大多数成员都将作为hash key,那么就应该在创建实例的时候就计算hash码,否则就应该在第一次调用hashCode的时候进行lazy initialize。finalizeprotected void finalize() throws Throwable当垃圾回收器确定不存在对该对象的更多引用时,对象的垃圾回收器调用该方法。 子类覆盖 finalize 方法,以配置系统资源进行其它清除。 finalize 方法抛出的任何异常导致暂停对象终止,但被其它对象忽略。 Object 中的 finialize 方法不执行任何操作。 抛出: Throwable finalize是位于Object类的一个方法,该方法的访问修饰符为protected,由于所有类为Object的子类,因此用户类很容易访问到这个方法。由于,finalize函数没有自动实现链式调用,我们必须手动的实现,因此finalize函数的最后一个语句通常是super.finalize()。通过这种方式,我们可以实现从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。很多Java初学者会认为这个方法类似与C++中的析构函数,将很多对象、资源的释放都放在这一函数里面。其实,这不是一种很好的方式。原因有三,其一,GC为了能够支持finalize函数,要对覆盖这个函数的对象作很多附加的工作。其二,在finalize运行完成之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用finalize会降低GC的运行性能。其三,由于GC调用finalize的时间是不确定的,因此通过这种方式释放资源也是不确定的。通常,finalize用于一些不容易控制、并且非常重要资源的释放,例如一些I/O的操作,数据的连接。这些资源的释放对整个应用程序是非常关键的。在这种情况下,程序员应该以通过程序本身管理(包括释放)这些资源为主,以finalize函数释放资源方式为辅,形成一种双保险的管理机制,而不应该仅仅依靠finalize来释放资源。下面给出一个例子说明,finalize函数被调用以后,仍然可能是可达的,同时也可说明一个对象的finalize只可能运行一次class MyObject{    Test main; //记录Test对象,在finalize中时用于恢复可达性    public MyObject(Test t)    {   main=t; //保存Test 对象    }    protected void finalize()    { main.ref=this;// 恢复本对象,让本对象可达 System.out.println("This is finalize");//用于测试finalize只运行一次    }}class Test { MyObject ref;  public static void main(String[] args) {   Test test=new Test();   test.ref=new MyObject(test);   test.ref=null; //MyObject对象为不可达对象,finalize将被调用   System.gc();    if (test.ref!=null) System.out.println("My Object还活着");  }}此例子中,需要注意的是虽然MyObject对象在finalize中变成可达对象,但是下次回收时候,finalize却不再被调用,因为finalize函数最多只调用一次。

            接口与内部类:    接口概述:接口不是类,而是对一组对类的要求,这些类要与接口一致。接口的任何方法都自动是public类型的。接口不能有实例,可以有没有实现的方法和常量,实现接口必须实现其方法。可以说接口是没有实例字段和方法的抽象类为什么使用接口不是使用具体的方法?java语言是强类型的,编译器会检查是否存在。可以声明接口变量,必须指向一个实现该接口的类的对象,例如comparable x.判断一个对象是否属于一个类:if(object instanceof Class),也可以检查对象是否实现某个接口:if(obj instanceof inserface)

            建立接口public interface interface_name2 [extends interface_name1]{  finalField;// 默认是publci static final Methods;/ ///默认是publci 但实现必须加public }//接口可以继承接口产生新的接口实现接口class Classson entends Classfathor implements interface1, interface2...{ ... public interface1Method{…} public interface2Method{…}…}Examples窗口接口WindowListenerpublic interface WindowAdapter{ public void windowClosing(WindowEvent e);}// Frame_obj.setDefaultCloseOperation(Frame_obj.EXIT_ON_CLOSE);//close window键盘接口public interface KeyListener{ public void KeyTyped(KeyEvent e);//压下并释放 public void KeyPressed(KeyEvent e);//压下 public void KeyReleased(KeyEvent e);//释放}点击接口public interface ActionListener{ public void actionPerformed(ActionEvent e); //有动作时}        抽象类与接口调用父类的构造super();抽象关键字abstract。类->抽象类->接口<抽象属性及其方法>抽象类->子类->不实现仍然是抽象类,不能事例化。接口->子类->可以不实现<可以多个接口>。使用接口就可以实现C++中的多重继承,但C++中多重继承太过复杂。        对象克隆 拷贝如果是拷贝对象,那么两对象是指向同一个类的同一个对象,不是真正的克隆。如果一个对象改变了,另一个对象也根着改变。Examples:Employee original=new Employee(“name”,5000);Employee copy=original;Copy.raiseSalary(10);// original也改变了。        克隆    浅拷贝如果是克隆对象,那么两对象是指向同一个类的同两个对象,这才是真正的克隆。如果一个对象改变了,另一个不改变。在该对象子对象中,基本数据类型没有指向关系,但要是对象类型,则是指向同一个类型的对象。Examples:Employee original=new Employee(“name”,5000);Employee copy=(Employee)original.clone();//clone返回的是Object,必须转换.Copy.raiseSalary(10);// original没有改变了。        深拷贝(如果有子对象就有必要使用)Method:protected Object clone()@see java.lang.Object该方法限制成保护类型,只能同类之间克隆Examples:Class Employee implements Cloneable{//在深拷贝中必须实现其接口,否则触发已检查异常 Public Object clone(){  Try{   Employee cloned=( Employee)super.clone();//对象克隆   Cloned.hireDay=(Date)hireDay.clone();//子对象克隆   Return cloned;  }catch(CloneNotSupportedException e){return null;} }}内部类InnerClass内部类定义在其他类的内部的类,原因有下:内部类对象能够访问创建它的对象的实现—包括那些私有数据。内部类能够隐藏起来,不为同一包中的其他类所见。匿名内部类可以方便的定义运行时回调。

    使用内部类在编写事件驱动的程序时用起来很方便 语法规则: 内部类往往在外部类的方法被实例化。 在内部类中,引用外部类规则:OuterClass.this.Field/Method;在外部类引用内部类的语法规则:ActionListener adder=this.new InnerClass(constructor parameters);在外部类作用范围内引用内部类(public)的语法:OuterClass.InnerClass下面的例子(见Examples): BankAccout mySavings=new BankAccout(1000); BankAccout.InterestAdder adder=mySavings.new InterestAdder(10);Examples: 计算银行汇率,按秒。外部类对象传递值到字段中,每秒触发一个接口变量所指向的对象(内部类对象),内部类调用外部类的字段,处理汇率,打印出。import java.awt.event.*;import java.text.*;import javax.swing.*;public class InnerClassTest{ public static void main(String args[]) {  BankAccout accout=new BankAccout(10000);//现金  accout.start(10);//汇率10%  JOptionPane.showMessageDialog(null,"quit");  System.exit(0); }}class BankAccout{ public BankAccout(double init) {  balance=init;//构造接受当前金额 } public void start(double rate)//汇率 {  ActionListener adder=new InterestAdder(rate);//接口变量指向一个对象  Timer t=new Timer(1000,adder);//触发条件  t.start();//run } private double balance;  private class InterestAdder implements ActionListener//实现接口 {  public InterestAdder(double aRate)//构造得到值,复制到内部类字段中  {   rate=aRate;//汇率  }  public void actionPerformed(ActionEvent event)//事件  {   double interest=balance*rate/100;//计算汇率   balance+=interest;//当前金额   NumberFormat formator=NumberFormat.getCurrencyInstance();//定植格式化   System.out.println("balance="+formator.format(balance));//格式输出  }  private double rate;//内部类字段;汇率 }}        局部内部类在方法中建立的类称局部内部类,局部类不会使用访问指示符(public /private)的声明,它们的范围总是限定在声明它们的程序块中。局部类的优点:能够对外部完全隐藏起来,即使是外部类也不能访问,除了该方法,没有任何方法会知道该类存在。局部类的方法只能使用那些声明为final的局部变量。Examples: public void start(final double rate)//汇率 {  class InterestAdder implements ActionListener//不能修饰该类  {   public void actionPerformed(ActionEvent event)//事件   {    double interest=balance*rate/100;//计算汇率    balance+=interest;//当前金额    NumberFormat formator=NumberFormat.getCurrencyInstance();      System.out.println("balance="+formator.format(balance));//格式输出   }  }

      ActionListener adder=new InterestAdder();//接口变量指向一个对象  Timer t=new Timer(1000,adder);//触发条件  t.start();//run }        匿名内部类格式:new SuperType(constructor parameters){Mathod/Data}超类也可以是接口:new InterfaceType(){Method/Data}由于构造器名是类名,而匿名是没有名字,所以匿名内部类不能有构造器,取而代之的是,构造参数被送到超类的构造器中。如果超类是接口,那么就没有参数。Examples: public void start(final double rate)//汇率 {   ActionListener adder=new ActionListener(){   public void actionPerformed(ActionEvent event)   {    double interest=balance*rate/100;    balance+=interest;    NumberFormat formator=NumberFormat.getCurrencyInstance();    System.out.println("balance="+formator.format(balance));   }};  Timer t=new Timer(1000,adder);  t.start(); }Examples: Person queen=new Person(“Mary”); Person count=new Person(“Dracula”){….}//内部类静态内部类Examples:public class StaticInnerClassTest{ public static void main(String args[]) {  double[] d=new double[20];  for(int i=0;i<d.length;i++)   d[i]=100*Math.random();  ArrayAlg.Pair p=ArrayAlg.minmax(d);//静态内部类对象接收一个内部类对象  System.out.println(p.getFirst());  System.out.println(p.getSecond()); } }class ArrayAlg//外部类{ public static class Pair//局部静态类 {  public Pair(double x,double y)  {   first=x;   second=y;  }  public double getFirst()  {return first;  }  public double getSecond(){return second;}  private double first;  private double second;  } public static Pair minmax(double[] temp)//返回一个类 {  if(temp.length==0) return new Pair(0,0);//返回到匿名构造  double min=temp[0];  double max=temp[0];  for(int i=0;i<temp.length;i++)  {   if(min>temp[i]) min=temp[i];   if(max<temp[i]) max=temp[i];  }  return new Pair(min,max); //返回到匿名构造 }}代理import java.lang.reflect. InvocationHandler;import java.lang.reflect.Proxy;

    代理类能够在运行时创建崭新的类,这样的代理类能够实现你指定的接口。尤其是代理类具有: 指定接口所要求的所有方法 Object类定义的所有方法。然而,不能在运行时为这些方法定义新的代码。必须提供一个调用处理器。调用处理器是实现了InvocationHandler接口的任意类对象。该接口只有一个方法: Object invoke(Object proxy,Method method,Object[] args)只要调用代理对象上的任意一方法,调用处理器的invoke方法就会被调用。创建一个代理对象,使用Proxy类中的newProxyInstance方法Examples:import java.lang.reflect.*;import java.util.*;public class ProxyTest{ public static void main(String args[]) {  Object[] elements=new Object[100];  for(int i=0;i<elements.length;i++)  {   Integer value=new Integer(i+1);   Class[] interfaces=value.getClass().getInterfaces();   InvocationHandler handler=new TraceHandler(value);   Object proxy=Proxy.newProxyInstance(null,interfaces,handler);   elements[i]=proxy;  }  Random generator=new Random();  int r=generator.nextInt(elements.length);  Integer key=new Integer(r+1);  int result=Arrays.binarySearch(elements,key);  if(result>=0)   System.out.println(elements[result]); } }class TraceHandler implements InvocationHandler{ public TraceHandler(Object t) {target=t;} public Object invoke(Object proxy,Method method,Object[] args)  throws Throwable {  System.out.print(target);  System.out.print("."+method.getName()+"(");  if(args!=null)  {   for(int i=0;i>args.length;i++)   {    System.out.print(args[i]);    if(i<args.length-1)     System.out.print(", ");   }  }  System.out.println(")");  return method.invoke(target,args); } private Object target;}


    最新回复(0)