[size=large]1.[b]对象的哪些属性与方法应该公开,哪些应该隐藏?[/b][/size]
[b]封装两大原则:[/b]
(1)把尽可能多的东西隐藏起来,对外提供简捷的接口;
(2)把所有属性隐藏起来;
[size=large][b]2.隐藏属性的好处?[/b][/size]
(1)能够灵活的控制属性的读与修改的访问级别(允许读与修改的public公开get、set方法,只允许读的public公开get方法,private隐藏set方法);
(2)防止使用者错误的修改属性(可以在set方法里添加必要的逻辑判断);
(3)有助于对象封装实现细节(如某属性的变化可能引起内部系统一列连锁变化,可以在set方法里添加相关逻辑)
[size=large][b]3.抽象的过程?[/b][/size]
问题领域的事物—〉(抽取与问题领域相关的事物的属性和行为)对象模型中的对象—〉(把具有相同属性和行为的对象抽象为)对象模型中的类—〉(从具有相同属性和行为的类抽象出)对象模型中的父类。
ps:abstract抽象类不能实例化。
[size=large][b]4.多态、动态绑定、静态绑定?[/b][/size]
[b]多态:[/b]一个接口可以有多个不同实现。
[b]动态绑定:[/b]Java虚拟机根据对象运行时的类型唤起继承机制,寻找调用方法的最佳匹配(意味着正在调用的方法是最佳方法),动态绑定不包括:
1.静态方法
2.私有方法(因为他们只能从内部调用,属于隐式的最终方法);
3.最终方法
[b]Java虚拟机采用以下绑定规则:[/b]
1.实例方法与引用变量实际引用的对象的方法绑定;
2.静态方法与引用变量所声明的类型的方法绑定;
3.成员变量(包括静态变量、实例变量)与应用变量所声明的类型的成员变量绑定;
[b]静态绑定:[/b]编译时绑定。
[b]重载方法的调用属于:[/b]静态绑定
[b]覆盖方法的调用属于:[/b]动态绑定
[size=large][b]5.继承与接口[/b][/size]
1.一个类只能继承一个直接父类,但是能实现多个接口;
原因:继承多个父类导致程序变得特别复杂、有背面向对象原则(封装)。
解决方法:采用内部类去集成需要继承的类,但不推荐这么做。
2.一个接口不能实现另一个接口,但是能集成多个其它接口;
注意:当子类的构造方法没有用super语句显示的调用父类的构造方法,而此时父类又没有提供默认构造方法时,将会出现编译错误,如下:
[size=large][b]6.类的生命周期[/b][/size]
[b]生命周期:[/b]
1.类的生命周期从类被加载,连接,初始化开始,到类被卸载结束;
2.类处于生命周期时,它的二进制数据位于运行时方法区内,在堆区还有一个相应的描述类的Class类的对象;
3.只有当类处于生命周期中时,Java程序才能够使用它。
[b]类的加载,连接和初始化[/b]
a.Java程序要使用某个类时,Java虚拟机要确保这个类被加载,连接和运行,其中连接包括验证,准备和解析。
[b]b.加载:[/b]查找并加载类的二进制数据;
[b]c.连接[/b]
[color=green]c.1 验证:[/color]确保加载类的正确性;
[color=green]c.2 准备:[/color]为静态变量分配内存,并将其初始化为默认值;
[color=green]c.3 解析:[/color]将类中的符号引用转换为直接引用。
[b]d.初始化:[/b]
给类的静态变量赋予正确的初始值
[b]首次使用一个类JVM才会对其进行初始化:[/b]
[color=green]六种首次使用场景:[/color]
1.创建类的实例,途经包括:使用new 创建对象实例,反射(java.lang.Class.newInstance())、clone、反序列化等;
2.调用类的静态方法
3.使用类或者接口的静态变量,给静态变量赋值;
4.初始化一个类的子类(本条不适合接口类);
5.调用JAVA API中某些反射方法,比如Class.forName("Worker");
6.把类声明为启动类,如: java Test01
[b]类加载器[/b]
[img]http://dl.iteye.com/upload/attachment/471985/562fc23e-7993-3e87-a02f-e4d2432724d0.jpg[/img]
[color=green]1.Bootstrap ClassLoader:[/color]用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。它的实现依赖与底层操作系统,属于JVM的实现的一部分,并没有继承java.lang.ClassLoader类。从系统属性sun.boot.class.path所指定目录加载类库。
[color=green]2.ExtClassLoader:[/color]用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ExtClassLoader.class 。此类由Bootstrap ClassLoader加载,但由于Bootstrap ClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.class的Parent(父加载器)被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变,或从系统属性java.ext.dirs所指定目录加载类库。
[color=green]3.AppClassLoader:[/color]系统类加载器(应用类加载器)用java实现,也是是Launcher.java的内部类,编译后的名字为:Launcher$AppClassLoader.class 。AppClassLoader是当Bootstrap ClassLoader加载完ExtClassLoader后,再被Bootstrap ClassLoader加载。所以ExtClassLoader和AppClassLoader都是被Bootstrap ClassLoader加载,但AppClassLoader的Parent被设置为ExtClassLoader。可见Parent和由哪个类加载器来加载不一定是对应的。这个类装载器是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,如果程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类都会由它来装载。而它的查找区域就是我们常常说到的Classpath,一旦建立其加载路径也不再改变。它从环境变量classpath或系统属性java.class.path所指定目录加载类库。
[color=green]4.User-Defined-ClassLoader:[/color]一般我们自定义的ClassLoader从ClassLoader类继承而来。比如:URLClassloader是ClassLoader的一个子类,而URLClassloader也是ExtClassLoader和AppClassLoader的父类(注意不是父加载器)。
[b]说明:[/b][color=green]类加载器之间的父子关系实际指的是加载器对象之间的包装关系,而不是类继承关系。一对父子加载器可能是同一个加载器类的两个实例。[/color]
[b]类加载父亲委托(parent delegation)机制[/b]
说明:在父亲委托机制中,各个加载器按照父子关系形成了树状结构,除了根类加载器以外,其余的类加载器有且只有一个父加载器。
假设loader2的父亲为loader1,loader1的父亲为系统类加载器。假设Java程序要求loader2加载Sample类,代码如下:
loader2首先从自己的命名空间中查找Sample类是否已经加载,如果已经加载,就直接返回代表Sample类的Class对象的引用。
如果Sample类还没有被加载,loader2首先请求loader1代为加载,loader1再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载。若根类加载器和扩展类加载器都不能加载,则系统类加载器尝试加载,若能加载成功,则将Sample类所对应的Class对象的引用返回给loader1,loader1再将引用返回给loader2,从而成功将Sample类加载进虚拟机。
若系统类加载器不能加载Smaple类,则loader1尝试加载Sample类,若loader1也不能成功加载,则loader2尝试加载。若所有的父加载器和loader2本身都不能加载,则抛出ClassNotFoundException异常。
[b]父亲委托机制的优点?[/b]
父亲委托机制的优点是能够提高软件系统的安全性。因为在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如,java.lang.Object类总是由根类加载器加载(BootStrap),其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。
[b]有几个重要概念需要理解一下。(与安全有关)[/b]
[color=green]1.命名空间:[/color]
每个类加载器都有自己的命名空间,命名空间由加载器及所有父加载器所加载的类组成。在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
[color=green]2.运行时包[/color]
由同一个类加载器加载的属于相同包的类组成运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同。只有属于同一运行时包的类才能互相访问可见(即默认访问级别)的类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类,去访问核心类库的包可见成员。假设用户自己定义了一个类java.lang.Spy,并由用户自定义的类加载器加载,由于jang.lang.Spy和核心类库java.lang.*由不同的加载器加载,它们属于不同的运行时包,所以java.lang.Spy不能访问核心类库java.lang包中的可见成员。
[b]类的卸载[/b]
当一个类的Class对象不再被应用,即不可触及时,Class对象就会结束生命周期,这个类在方法区内的数据也会被卸载,从而结束此类的生命周期。
说明:由JVM自带的类加载器所加载的类,在JVM的生命周期中,始终不会被卸载。
[b]运行时数据区[/b]
[img]http://dl.iteye.com/upload/attachment/472033/550ff3af-b002-3d6e-9d49-6c747f3d31a2.jpg[/img]
[size=large][b]7.垃圾回收(Garbage Collection)[/b][/size]
垃圾回收具有以下特点:
(1)只有当对象不再被程序中的任何引用变量引用时,它的内存才可能被回收。
(2)程序无法迫使垃圾回收器立即执行垃圾回收操作。当一个对象处于可复活状态(当程序不再有任何引用变量引用Sample对象时,它就进入可复活状态)时,垃圾回收线程何时执行它的finalize()方法,何时使它转到不可触及状态(当Java虚拟机执行完所有可复活对象的finalize()方法后,假如这些方法都没有使Sample对象转到可触及状态,那么Sample对象就进入不可触及状态。只有当对象处于不可触及状态时,垃圾回收器才会真正回收它占用的内存),何时回收它占用的内存,这对于程序来说都是透明的。
(3)程序中可以调用System.gc()或者Runtime.gc()方法提示垃圾回收器尽快执行垃圾回收操作,但是这也不能保证调用完该方法后,垃圾回收线程就立即执行回收操作,而且不能保证垃圾回收线程一定会执行这一操作。
(4)垃圾收集发生的不可预知性:由于实现了不同的垃圾收集算法和采用了不同的收集机制,所以它有可能是定时发生,有可能是当出现系统空闲CPU资源时发生,也有可能是和原始的垃圾收集一样,等到内存消耗出现极限时发生,这与垃圾收集器的选择和具体的设置都有关系。
(5)垃圾收集的精确性:主要包括2 个方面:(a)垃圾收集器能够精确标记活着的对象;(b)垃圾收集器能够精确地定位对象之间的引用关系。前者是完全地回收所有废弃对象的前提,否则就可能造成内存泄漏。而后者则是实现归并和复制等算法的必要条件。所有不可达对象都能够可靠地得到回收,所有对象都能够重新分配,允许对象的复制和对象内存的缩并,这样就有效地防止内存的支离破碎。
(6)现在有许多种不同的垃圾收集器,每种有其算法且其表现各异,既有当垃圾收集开始时就停止应用程序的运行,又有当垃圾收集开始时也允许应用程序的线程运行,还有在同一时间垃圾收集多线程运行。
(7)垃圾收集的实现和具体的JVM 以及JVM的内存模型有非常紧密的关系。不同的JVM可能采用不同的垃圾收集,而JVM 的内存模型决定着该JVM可以采用哪些类型垃圾收集。现在,HotSpot 系列JVM中的内存系统都采用先进的面向对象的框架设计,这使得该系列JVM都可以采用最先进的垃圾收集。
(8)随着技术的发展,现代垃圾收集技术提供许多可选的垃圾收集器,而且在配置每种收集器的时候又可以设置不同的参数,这就使得根据不同的应用环境获得最优的应用性能成为可能。
[b]封装两大原则:[/b]
(1)把尽可能多的东西隐藏起来,对外提供简捷的接口;
(2)把所有属性隐藏起来;
[size=large][b]2.隐藏属性的好处?[/b][/size]
(1)能够灵活的控制属性的读与修改的访问级别(允许读与修改的public公开get、set方法,只允许读的public公开get方法,private隐藏set方法);
(2)防止使用者错误的修改属性(可以在set方法里添加必要的逻辑判断);
(3)有助于对象封装实现细节(如某属性的变化可能引起内部系统一列连锁变化,可以在set方法里添加相关逻辑)
[size=large][b]3.抽象的过程?[/b][/size]
问题领域的事物—〉(抽取与问题领域相关的事物的属性和行为)对象模型中的对象—〉(把具有相同属性和行为的对象抽象为)对象模型中的类—〉(从具有相同属性和行为的类抽象出)对象模型中的父类。
ps:abstract抽象类不能实例化。
[size=large][b]4.多态、动态绑定、静态绑定?[/b][/size]
[b]多态:[/b]一个接口可以有多个不同实现。
[b]动态绑定:[/b]Java虚拟机根据对象运行时的类型唤起继承机制,寻找调用方法的最佳匹配(意味着正在调用的方法是最佳方法),动态绑定不包括:
1.静态方法
2.私有方法(因为他们只能从内部调用,属于隐式的最终方法);
3.最终方法
[b]Java虚拟机采用以下绑定规则:[/b]
1.实例方法与引用变量实际引用的对象的方法绑定;
2.静态方法与引用变量所声明的类型的方法绑定;
3.成员变量(包括静态变量、实例变量)与应用变量所声明的类型的成员变量绑定;
[b]静态绑定:[/b]编译时绑定。
[b]重载方法的调用属于:[/b]静态绑定
[b]覆盖方法的调用属于:[/b]动态绑定
[size=large][b]5.继承与接口[/b][/size]
1.一个类只能继承一个直接父类,但是能实现多个接口;
原因:继承多个父类导致程序变得特别复杂、有背面向对象原则(封装)。
解决方法:采用内部类去集成需要继承的类,但不推荐这么做。
2.一个接口不能实现另一个接口,但是能集成多个其它接口;
注意:当子类的构造方法没有用super语句显示的调用父类的构造方法,而此时父类又没有提供默认构造方法时,将会出现编译错误,如下:
class D {
int i;
D(int i) {
this.i = i * 2;
}
}
class E extends D {
E(int i) {
/*
* If there is no super(i) , it will compile failed :Implicit super
* constructor D() is undefined. Must explicitly invoke another
* constructor.
*/
super(i);
System.out.println();
}
}
[size=large][b]6.类的生命周期[/b][/size]
[b]生命周期:[/b]
1.类的生命周期从类被加载,连接,初始化开始,到类被卸载结束;
2.类处于生命周期时,它的二进制数据位于运行时方法区内,在堆区还有一个相应的描述类的Class类的对象;
3.只有当类处于生命周期中时,Java程序才能够使用它。
[b]类的加载,连接和初始化[/b]
a.Java程序要使用某个类时,Java虚拟机要确保这个类被加载,连接和运行,其中连接包括验证,准备和解析。
[b]b.加载:[/b]查找并加载类的二进制数据;
[b]c.连接[/b]
[color=green]c.1 验证:[/color]确保加载类的正确性;
[color=green]c.2 准备:[/color]为静态变量分配内存,并将其初始化为默认值;
[color=green]c.3 解析:[/color]将类中的符号引用转换为直接引用。
[b]d.初始化:[/b]
给类的静态变量赋予正确的初始值
[b]首次使用一个类JVM才会对其进行初始化:[/b]
[color=green]六种首次使用场景:[/color]
1.创建类的实例,途经包括:使用new 创建对象实例,反射(java.lang.Class.newInstance())、clone、反序列化等;
2.调用类的静态方法
3.使用类或者接口的静态变量,给静态变量赋值;
4.初始化一个类的子类(本条不适合接口类);
5.调用JAVA API中某些反射方法,比如Class.forName("Worker");
6.把类声明为启动类,如: java Test01
[b]类加载器[/b]
[img]http://dl.iteye.com/upload/attachment/471985/562fc23e-7993-3e87-a02f-e4d2432724d0.jpg[/img]
[color=green]1.Bootstrap ClassLoader:[/color]用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。它的实现依赖与底层操作系统,属于JVM的实现的一部分,并没有继承java.lang.ClassLoader类。从系统属性sun.boot.class.path所指定目录加载类库。
[color=green]2.ExtClassLoader:[/color]用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ExtClassLoader.class 。此类由Bootstrap ClassLoader加载,但由于Bootstrap ClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.class的Parent(父加载器)被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变,或从系统属性java.ext.dirs所指定目录加载类库。
[color=green]3.AppClassLoader:[/color]系统类加载器(应用类加载器)用java实现,也是是Launcher.java的内部类,编译后的名字为:Launcher$AppClassLoader.class 。AppClassLoader是当Bootstrap ClassLoader加载完ExtClassLoader后,再被Bootstrap ClassLoader加载。所以ExtClassLoader和AppClassLoader都是被Bootstrap ClassLoader加载,但AppClassLoader的Parent被设置为ExtClassLoader。可见Parent和由哪个类加载器来加载不一定是对应的。这个类装载器是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,如果程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类都会由它来装载。而它的查找区域就是我们常常说到的Classpath,一旦建立其加载路径也不再改变。它从环境变量classpath或系统属性java.class.path所指定目录加载类库。
[color=green]4.User-Defined-ClassLoader:[/color]一般我们自定义的ClassLoader从ClassLoader类继承而来。比如:URLClassloader是ClassLoader的一个子类,而URLClassloader也是ExtClassLoader和AppClassLoader的父类(注意不是父加载器)。
[b]说明:[/b][color=green]类加载器之间的父子关系实际指的是加载器对象之间的包装关系,而不是类继承关系。一对父子加载器可能是同一个加载器类的两个实例。[/color]
[b]类加载父亲委托(parent delegation)机制[/b]
说明:在父亲委托机制中,各个加载器按照父子关系形成了树状结构,除了根类加载器以外,其余的类加载器有且只有一个父加载器。
假设loader2的父亲为loader1,loader1的父亲为系统类加载器。假设Java程序要求loader2加载Sample类,代码如下:
Class sampleClass = loader2.loadClass("Sample");
loader2首先从自己的命名空间中查找Sample类是否已经加载,如果已经加载,就直接返回代表Sample类的Class对象的引用。
如果Sample类还没有被加载,loader2首先请求loader1代为加载,loader1再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载。若根类加载器和扩展类加载器都不能加载,则系统类加载器尝试加载,若能加载成功,则将Sample类所对应的Class对象的引用返回给loader1,loader1再将引用返回给loader2,从而成功将Sample类加载进虚拟机。
若系统类加载器不能加载Smaple类,则loader1尝试加载Sample类,若loader1也不能成功加载,则loader2尝试加载。若所有的父加载器和loader2本身都不能加载,则抛出ClassNotFoundException异常。
[b]父亲委托机制的优点?[/b]
父亲委托机制的优点是能够提高软件系统的安全性。因为在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如,java.lang.Object类总是由根类加载器加载(BootStrap),其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。
[b]有几个重要概念需要理解一下。(与安全有关)[/b]
[color=green]1.命名空间:[/color]
每个类加载器都有自己的命名空间,命名空间由加载器及所有父加载器所加载的类组成。在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
[color=green]2.运行时包[/color]
由同一个类加载器加载的属于相同包的类组成运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同。只有属于同一运行时包的类才能互相访问可见(即默认访问级别)的类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类,去访问核心类库的包可见成员。假设用户自己定义了一个类java.lang.Spy,并由用户自定义的类加载器加载,由于jang.lang.Spy和核心类库java.lang.*由不同的加载器加载,它们属于不同的运行时包,所以java.lang.Spy不能访问核心类库java.lang包中的可见成员。
[b]类的卸载[/b]
当一个类的Class对象不再被应用,即不可触及时,Class对象就会结束生命周期,这个类在方法区内的数据也会被卸载,从而结束此类的生命周期。
说明:由JVM自带的类加载器所加载的类,在JVM的生命周期中,始终不会被卸载。
[b]运行时数据区[/b]
[img]http://dl.iteye.com/upload/attachment/472033/550ff3af-b002-3d6e-9d49-6c747f3d31a2.jpg[/img]
[size=large][b]7.垃圾回收(Garbage Collection)[/b][/size]
垃圾回收具有以下特点:
(1)只有当对象不再被程序中的任何引用变量引用时,它的内存才可能被回收。
(2)程序无法迫使垃圾回收器立即执行垃圾回收操作。当一个对象处于可复活状态(当程序不再有任何引用变量引用Sample对象时,它就进入可复活状态)时,垃圾回收线程何时执行它的finalize()方法,何时使它转到不可触及状态(当Java虚拟机执行完所有可复活对象的finalize()方法后,假如这些方法都没有使Sample对象转到可触及状态,那么Sample对象就进入不可触及状态。只有当对象处于不可触及状态时,垃圾回收器才会真正回收它占用的内存),何时回收它占用的内存,这对于程序来说都是透明的。
(3)程序中可以调用System.gc()或者Runtime.gc()方法提示垃圾回收器尽快执行垃圾回收操作,但是这也不能保证调用完该方法后,垃圾回收线程就立即执行回收操作,而且不能保证垃圾回收线程一定会执行这一操作。
(4)垃圾收集发生的不可预知性:由于实现了不同的垃圾收集算法和采用了不同的收集机制,所以它有可能是定时发生,有可能是当出现系统空闲CPU资源时发生,也有可能是和原始的垃圾收集一样,等到内存消耗出现极限时发生,这与垃圾收集器的选择和具体的设置都有关系。
(5)垃圾收集的精确性:主要包括2 个方面:(a)垃圾收集器能够精确标记活着的对象;(b)垃圾收集器能够精确地定位对象之间的引用关系。前者是完全地回收所有废弃对象的前提,否则就可能造成内存泄漏。而后者则是实现归并和复制等算法的必要条件。所有不可达对象都能够可靠地得到回收,所有对象都能够重新分配,允许对象的复制和对象内存的缩并,这样就有效地防止内存的支离破碎。
(6)现在有许多种不同的垃圾收集器,每种有其算法且其表现各异,既有当垃圾收集开始时就停止应用程序的运行,又有当垃圾收集开始时也允许应用程序的线程运行,还有在同一时间垃圾收集多线程运行。
(7)垃圾收集的实现和具体的JVM 以及JVM的内存模型有非常紧密的关系。不同的JVM可能采用不同的垃圾收集,而JVM 的内存模型决定着该JVM可以采用哪些类型垃圾收集。现在,HotSpot 系列JVM中的内存系统都采用先进的面向对象的框架设计,这使得该系列JVM都可以采用最先进的垃圾收集。
(8)随着技术的发展,现代垃圾收集技术提供许多可选的垃圾收集器,而且在配置每种收集器的时候又可以设置不同的参数,这就使得根据不同的应用环境获得最优的应用性能成为可能。