Java 7之基础 - 学习Java类必须知道的几点
来源:互联网 发布:python分布式读取 编辑:程序博客网 时间:2024/05/21 14:09
1、类的加载执行顺序
public class Dervied extends Base { private String name = "dervied"; public Dervied() { tellName(); printName(); } public void tellName() { System.out.println("Dervied tell name: " + name); } public void printName() { System.out.println("Dervied print name: " + name); } public static void main(String[] args){ new Dervied(); }}class Base { private String name = "base"; public Base() { tellName(); printName(); } public void tellName() { System.out.println("Base tell name: " + name); } public void printName() { System.out.println("Base print name: " + name); }}
先初始化父类然后再初始化子类(这个初始化包括静态和非静态变量、静态和非静态的方法块、构造函数)
Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied
再看一下如下的例子:
class ParentClass {public static int a=2;public int b=3;{System.out.println("this is anonymity b="+b);}static {a=4;System.out.println("this is static and a="+a);}public ParentClass() {System.out.println("this is parent gozao");this.s();}public void s() {System.out.println("this is parent");}}public class Son extends ParentClass {public Son(){System.out.println("this is son gozao");}public static void main(String[] args) {ParentClass d = new Son();d.s();}public void s() {//super.s();System.out.println("this is son");}}
运行结果如下:
this is static and a=4this is anonymity b=3this is parent gozaothis is sonthis is son gozaothis is son
可以看出类内的加载顺序为:
(1)
静态变量 对于静态变量肯定要首先进行初始化,因为后面的方法可能会使用这个变量,或者构造函数中也可能用到。而对于非静态变量而言,由于匿名块内、非静态方法和构造函数都可以进行操作(不仅仅是初始化),所以要提前进行加载和赋默认值。
静态代码块 多个静态代码块按顺序加载,这里需要注意:在这个顺序不难是类内书写的顺序,也是类加载的顺序,也就是说如果子类也有静态代码块,则子类的也加载。由于静态代码块可能会负责变量的初始化,或者是对象等的初始化,这样在构造函数或者方法中就变得可用了。而顺序加载多半是由于Java是按顺序执行代码的原因。
静态方法 一般静态方法中禁止引用还未初始化的非静态变量,如果引用了静态变量,则静态变量必须放到这个静态方法的前面,以保证在使用时已经被正确地初始化。
一般如上要按顺序执行加载。
如果静态代码块中用到了静态变量,则静态变量必须在前面,如果在后会出现编译错误,而静态代码块中不可以出现非静态的变量。
public static int a=2; // 必须放到静态代码块前// public int a=3; // 代码块中会报错{System.out.println("this is anonymity b="+b);}static {System.out.println("this is static and a="+a);}静态方法与静态变量的关系和上面一样。
(2)
匿名代码块 这个要后初始化于静态代码块和静态变量,因为其依然属于实例对象,而不属于类。在这里可以对非静态成员变量进行初始化工作,同样也可以引用静态变量,因为已经被初始化过了。
非静态变量 这个要后初始化于静态代码块和静态变量,如果在匿名代码块中有对非静态变量的引用,则非静态变量必须在前面,以保证先被初始化。对静态变量的位置不做要求。举例:
public int b=3; // 必须放到匿名代码块的前面,以保证先被初始化后使用{System.out.println("this is anonymity b="+b);}public static int b=2; // 可以放到匿名代码块的任意位置
如果两者没有引用,则按顺序执行加载。
(3)
构造函数 这里需要解释一下,为什么初始化子类必先初始化父类,由于子类可能会继承父类的属性或方法,所以肯定要先初始化父类了,而初始化父类则必须要调用父类的构造函数。
至于方法不用考虑,因为方法不用初始化,所以无论是静态还是不静态,和这个没有关系。
其实如上的代码还能说明一些问题,可以看到,在父类中通过this.s()调用的是子类的方法,子类的s()方法覆盖了父类的方法后,无论在哪里调用,都是调用子类的方法。
下面再来看一个比较复杂的面试题(阿里巴巴),如下:
public class InitializeDemo {private static int k = 1;private static InitializeDemo t1 = new InitializeDemo("t1");private static InitializeDemo t2 = new InitializeDemo("t2");private static int i = print("i");private static int n = 99;static {print("静态块");}private int j = print("j");{print("构造块");}public InitializeDemo(String str) {System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);++i;++n;}public static int print(String str) {System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);++n;return ++i;}public static void main(String args[]) {new InitializeDemo("init");}}最终的输出结果如下:
1:j i=0 n=02:构造块 i=1 n=13:t1 i=2 n=24:j i=3 n=35:构造块 i=4 n=46:t2 i=5 n=57:i i=6 n=68:静态块 i=7 n=999:j i=8 n=10010:构造块 i=9 n=10111:init i=10 n=102对其进行解释如下:
1. 运行main方法的时候,JVM会调用ClassLoader来加载Test类,那么一起源于这次加载2. 上面有四个静态属性,所以会按顺序逐一初始化这四个静态属性3.private static int k = 1; 此时将k初始化为14.private static Test t1 = new Test("t1"); 创建Test对象,那么按照核心理念中的顺序 先执行 private int j = print("j"); 打印出j,然后执行构造块,最后执行构造方法5.private static Test t2 = new Test("t2"); 同步骤46.private static int i = print("i"); 打印i7.private static int n = 99; 直到这一步,n才被赋值为99,之前是从默认的0开始++的8. 静态属性初始化完毕,代码走到静态块,打印出静态块,此时n=999. 静态属性和静态块执行完毕,然后执行main方法中的代码new Test("init");10.main方法中创建对象,先初始化非静态属性,private int j = print("j");打印j,然后执行构造块,最后执行构造方法
2、不可变类
不可变类是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如Integer、Long等都是不可变类,除此之外还有java.lang.String也是不可变类。不可变类的实例一但创建,其内在成员变量的值就不能被修改。创建一个不可变类需要如下条件:
- 对于一般成员都是private,还可以使用public static final 来定义一个全局的常量。
- 不提供对成员的修改方法,例如:setXxx()
- 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有的类方法加上final关键字(弱不可变类)。
- 如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化或者get方法时通过深度clone方法,来确保类的不可变。
其它的都好理解,下面来着重解释一下第4条。举个例子:
public final class MyImmutableWrong { // 使用final关键字声明为强不可变类private final int[] myArray;public MyImmutableWrong(int[] anArray) {this.myArray = anArray; // wrong}public String toString() {StringBuffer sb = new StringBuffer("Numbers are: ");for (int i = 0; i < myArray.length; i++) {sb.append(myArray[i] + " ");}return sb.toString();}}由于int[] anArray是一个数组,属于引用类型。这样当再次去操作外部的一个引用时,其指向的共同内容,也就是数组的值。
public class dd {public static void main(String[] args) { int array[]={1,2,3,4}; MyImmutableWrong service=new MyImmutableWrong(array); array[2]=99; System.out.println(service.toString());}}查看控制台的输出内容:
Numbers are: 1 2 99 4
可以看到,不可变类中的数组内容发生了改变。究其原因就是 - 关键字final仅对其直接指向的对象有用,并且final引用可以指向带有非final域的对象。
为了避免这个问题,必须对数组进行深度克隆。也就是专门再为不可变类中的数组开避一份内存空间,然后将参数的值赋值过去。正确的写法如下:
public final class MyImmutableCorrect {private final int[] myArray;public MyImmutableCorrect(int[] anArray) {this.myArray = anArray.clone(); }public String toString() {StringBuffer sb = new StringBuffer("Numbers are: ");for (int i = 0; i < myArray.length; i++) {sb.append(myArray[i] + " ");}return sb.toString();}}测试后发现:
Numbers are: 1 2 3 4
不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。
3、静态内部类
内部类就是在一个类的内部定义的类,内部类中不能定义静态成员(静态成员不是对象的特性,只是为了找一个容身之处,所以需要放到一个类中而已,把“全局变量”放在内部类中就是毫无意义的事情,所以被禁止),内部类可以直接访问外部类中的成员变量,即使这个成员变量是由private来修饰的。
同样拿不可变类来举一个例子。假如这个不可变类中有许多的private final属性需要初始化,这时候看起来就不太方便。可能有人会采用工厂方法来解决,但有时候也可以使用静态内部类来解决这一问题。如下:
public class Author { private final String name; public String getName() { return name; } public Author(String name_) { name = name_; } @Override public String toString() { return "Author [name=" + name + "]"; }}
public final class Update { // 强不可变类private final Author author; // 作者,是个引用变量private final String updateText; // 更新内容private final long createTime; // 更新时间// 私有构造函数,防止外部实例化private Update(Builder b_) {author = b_.author;updateText = b_.updateText;createTime = b_.createTime;}// 构建器public static class Builder {private long createTime;private Author author;private String updateText;public Builder author(Author author_) {author = author_;return this;}public Builder updateText(String updateText_) {updateText = updateText_;return this;}public Builder createTime(long createTime_) {createTime = createTime_;return this;}public Update build() {// 更新外部类的值return new Update(this);}}public Author getAuthor() {return author;}public String getUpdateText() {return updateText;} @Override public String toString() { return "Update [author=" + author + ", updateText=" + updateText + ", createTime=" + createTime + "]"; }}可以看到,静态内部类有与外部类同样的成员变量,但是静态内部类中的成员变量却是可以修改的。
来测试一下:
public class Test {public static void main(String[] args) {final Update first = getUpdate("abc");// 获取不可变类的实例System.out.println(first.toString());}private static Update getUpdate(String s) {Update.Builder b = new Update.Builder();b.updateText(s).author(new Author("mazhi"));return b.build();// 初始化不可变类并返回实例}}运行后的结果如下:
Update [author=Author [name=mazhi], updateText=abc, createTime=0]
在方法外部定义的内部类可以加上static关键字、可以定义成public、protected、默认的、private等多种类型,而普通类只能定义成public和默认的这两种类型。在外面引用静态内部类的名称为"外部类名.内部类名"。不需要创建外部类的实例对象,就可以直接创建静态内部类。例如:
Update.Builder b = new Update.Builder(); // 获取静态内部类的实例对象
由于不依赖于外部类的实例对象,所以能访问外部类的非static成员变量。
想了解更多的内部类,可以查阅其它资料。
参考文献:
1、http://blog.csdn.net/ysjian_pingcx/article/details/19605335
3 0
- Java 7之基础 - 学习Java类必须知道的几点
- 学习Java类必须知道的几点
- 机器学习你必须知道的几点知识
- 学习java必须知道的几个东西
- Java程序员成长之路(你必须知道的技术点)
- Java程序员成长之路(你必须知道的技术点)
- 想要成为java大牛必须知道的25点
- 想要成为java大牛必须知道的25点
- 想要成为java大牛必须知道的25点
- 想要成为java大牛必须知道的25点
- 成为java大牛必须知道的25点
- java学习之二——关于java你必须知道的一些常识
- 你必须知道的几种java容器(集合类)
- 你必须知道的几种java容器(集合类)
- 你必须知道的几种java容器(集合类)
- 谈学习:合格的编程自学者必须知道的几点
- java基础(一)-----学习system类的几点收获(一)
- java基础(二)------学习system类的几点收获(二)
- 公开课读书笔记:无所不在的移动通信(3)-如何才能守住我
- 【OpenMAX】 IL 1.1.2 原子类型中的原子数据类型
- Android之Loader理解
- JAVA中的下拉框(Combo Box)概述(1)
- Android事件传递机制2
- Java 7之基础 - 学习Java类必须知道的几点
- hdu 1240 Asteroids! (广搜)
- Java EE 中的请求和响应编码问题 - 相关API汇总
- 【深入浅出Objective-C】8.2数组(NSArray)和集合(NSSet)
- Python学习笔记(八)异常
- 使用bower安装angular-ui-router时release目录为空
- TSX之有限事务内存,TSX之锁消除技术 和 lock free 编程之比较(有效总结)
- 全网视频网站破解序言
- Struts2国际化