学习Java类必须知道的几点

来源:互联网 发布:sql向表中更改字段 编辑:程序博客网 时间:2024/04/30 14:21


1、类的加载执行顺序 

看一下如下的例子:

[java] view plaincopyprint?

  1. class ParentClass {  

  2.     public static int  a=2;  

  3.     public int b=3;  

  4.     {  

  5.         System.out.println("this is anonymity b="+b);  

  6.     }  

  7.     static {  

  8.         a=4;  

  9.         System.out.println("this is static and a="+a);  

  10.     }  

  11.     public ParentClass() {  

  12.         System.out.println("this is parent gozao");  

  13.         this.s();  

  14.     }  

  15.     public void s() {  

  16.         System.out.println("this is parent");  

  17.     }  

  18. }  

  19. public class Son extends ParentClass {  

  20.     public Son(){  

  21.         System.out.println("this is  son gozao");  

  22.     }  

  23.     public static void main(String[] args) {  

  24.         ParentClass d = new Son();  

  25.         d.s();  

  26.     }  

  27.     public void s() {  

  28.         //super.s();  

  29.         System.out.println("this is son");  

  30.     }  

  31. }  

运行结果如下:

[java] view plaincopyprint?

  1. this is static and a=4  

  2. this is anonymity   

  3. this is parent gozao  

  4. this is son  

  5. this is  son gozao  

  6. this is son  

可以看出类内的加载顺序为:

(1)初始化变量。对于静态变量肯定要首先进行初始化,因为后面的方法可能会使用这个变量,或者构造函数中也可能用到。而对于非静态变量而言,由于匿名块内、非静态方法和构造函数都可以进行操作(不仅仅是初始化),所以要提前进行加载和赋默认值。

(2)初始化静态代码块,多个静态代码块按顺序加载,这里需要注意:在这个顺序不难是类内书写的顺序,也是类加载的顺序,也就是说如果子类也有静态代码块,则子类的也加载。由于静态代码块可能会负责变量的初始化,或者是对象等的初始化,这样在构造函数或者方法中就变得可用了。而顺序加载多半是由于Java是按顺序执行代码的原因。

(3)匿名代码块,这个要后初始化于静态代码块,因为其依然属于实例对象,而不属于类。在这里可以对非静态成员变量进行初始化工作。

(4)构造函数 这里需要解释一下,为什么初始化子类必先初始化父类,由于子类可能会继承父类的属性或方法,所以肯定要先初始化父类了,而初始化父类则必须要调用父类的构造函数。

至于方法不用考虑,因为方法不用初始化,所以无论是静态还是不静态,和这个没有关系。

其实如上的代码还能说明一些问题,可以看到,在父类中通过this.s()调用的是子类的方法,子类的s()方法覆盖了父类的方法后,无论在哪里调用,都是调用子类的方法。

2、不可变类  

不可变类是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如Integer、Long等都是不可变类,除此之外还有java.lang.String也是不可变类。不可变类的实例一但创建,其内在成员变量的值就不能被修改。创建一个不可变类需要如下条件:

1. 对于一般成员都是private,还可以使用public static final 来定义一个全局的常量。
2. 不提供对成员的修改方法,例如:setXXX()
3. 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有的类方法加上final关键字(弱不可变类)。
4. 如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化或者get方法时通过深度clone方法,来确保类的不可变。

其它的都好理解,下面来着重解释一下第4条。举个例子:

[cpp] view plaincopyprint?

  1. public final class MyImmutableWrong {  // 使用final关键字声明为强不可变类  

  2.     private final int[] myArray;  

  3.   

  4.     public MyImmutableWrong(int[] anArray) {  

  5.         this.myArray = anArray; // wrong  

  6.     }  

  7.   

  8.     public String toString() {  

  9.         StringBuffer sb = new StringBuffer("Numbers are: ");  

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

  11.             sb.append(myArray[i] + " ");  

  12.         }  

  13.         return sb.toString();  

  14.     }  

  15. }  

由于int[] anArray是一个数组,属于引用类型。这样当再次去操作外部的一个引用时,其指向的共同内容,也就是数组的值。

[java] view plaincopyprint?

  1. public class dd {  

  2.     public static void main(String[] args) {  

  3.          int array[]={1,2,3,4};  

  4.          MyImmutableWrong service=new MyImmutableWrong(array);  

  5.          array[2]=99;  

  6.          System.out.println(service.toString());  

  7.     }  

  8.   

  9. }  

查看控制台的输出内容:Numbers are: 1 2 99 4 

可以看到,不可变类中的数组内容发生了改变。究其原因就是 - 关键字final仅对其直接指向的对象有用,并且final引用可以指向带有非final域的对象。

为了避免这个问题,必须对数组进行深度克隆。也就是专门再为不可变类中的数组开避一份内存空间,然后将参数的值赋值过去。正确的写法如下:

[java] view plaincopyprint?

  1. public final class MyImmutableCorrect {  

  2.     private final int[] myArray;  

  3.   

  4.     public MyImmutableCorrect(int[] anArray) {  

  5.         this.myArray = anArray.clone();   

  6.     }  

  7.   

  8.     public String toString() {  

  9.         StringBuffer sb = new StringBuffer("Numbers are: ");  

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

  11.             sb.append(myArray[i] + " ");  

  12.         }  

  13.         return sb.toString();  

  14.     }  

  15. }  

测试后发现:Numbers are: 1 2 3 4 


不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。

3、静态内部类  

       内部类就是在一个类的内部定义的类,内部类中不能定义静态成员(静态成员不是对象的特性,只是为了找一个容身之处,所以需要放到一个类中而已,把“全局变量”放在内部类中就是毫无意义的事情,所以被禁止),内部类可以直接访问外部类中的成员变量,即使这个成员变量是由private来修饰的。

       同样拿不可变类来举一个例子。假如这个不可变类中有许多的private final属性需要初始化,这时候看起来就不太方便。可能有人会采用工厂方法来解决,但有时候也可以使用静态内部类来解决这一问题。如下:

[java] view plaincopyprint?

  1. public class Author {  

  2.   

  3.   private final String name;  

  4.   

  5.   public String getName() {  

  6.     return name;  

  7.   }  

  8.   

  9.   public Author(String name_) {  

  10.     name = name_;  

  11.   }  

  12.   @Override  

  13.   public String toString() {  

  14.     return "Author [name=" + name + "]";  

  15.   }  

  16. }  

[java] view plaincopyprint?

  1. public final class Update {  // 强不可变类  

  2.   

  3.     private final Author author;  // 作者,是个引用变量  

  4.     private final String updateText;  // 更新内容  

  5.     private final long createTime;  // 更新时间  

  6.       

  7.     // 私有构造函数,防止外部实例化  

  8.     private Update(Builder b_) {  

  9.         author = b_.author;  

  10.         updateText = b_.updateText;  

  11.         createTime = b_.createTime;  

  12.     }  

  13.     // 构建器  

  14.     public static class Builder {  

  15.         private long createTime;  

  16.         private Author author;  

  17.         private String updateText;  

  18.   

  19.         public Builder author(Author author_) {  

  20.             author = author_;  

  21.             return this;  

  22.         }  

  23.   

  24.         public Builder updateText(String updateText_) {  

  25.             updateText = updateText_;  

  26.             return this;  

  27.         }  

  28.   

  29.         public Builder createTime(long createTime_) {  

  30.             createTime = createTime_;  

  31.             return this;  

  32.         }  

  33.   

  34.         public Update build() {// 更新外部类的值  

  35.             return new Update(this);  

  36.         }  

  37.     }  

  38.     public Author getAuthor() {  

  39.         return author;  

  40.     }  

  41.   

  42.     public String getUpdateText() {  

  43.         return updateText;  

  44.     }  

  45.       

  46.      @Override  

  47.       public String toString() {  

  48.         return "Update [author=" + author + ", updateText=" + updateText  

  49.             + ", createTime=" + createTime + "]";  

  50.       }  

  51. }  

可以看到,静态内部类有与外部类同样的成员变量,但是静态内部类中的成员变量却是可以修改的。

来测试一下:

[java] view plaincopyprint?

  1. public class Test {  

  2.   

  3.     public static void main(String[] args) {  

  4.         final Update first = getUpdate("abc");// 获取不可变类的实例  

  5.         System.out.println(first.toString());  

  6.     }  

  7.   

  8.     private static Update getUpdate(String s) {  

  9.         Update.Builder b = new Update.Builder();  

  10.         b.updateText(s).author(new Author("mazhi"));  

  11.         return b.build();// 初始化不可变类并返回实例  

  12.     }  

  13. }  

运行后的结果如下:
Update [author=Author [name=mazhi], updateText=abc, createTime=0]

   在方法外部定义的内部类可以加上static关键字、可以定义成public、protected、默认的、private等多种类型,而普通类只能定义成public和默认的这两种类型。在外面引用静态内部类的名称为"外部类名.内部类名"。不需要创建外部类的实例对象,就可以直接创建静态内部类。例如:


[java] view plaincopyprint?

  1. Update.Builder b = new Update.Builder(); // 获取静态内部类的实例对象  


   由于不依赖于外部类的实例对象,所以能访问外部类的非static成员变量。

想了解更多的内部类,可以查阅其它资料。

0 0
原创粉丝点击