------- android培训、java培训、期待与您交流! ----------
一、JDk1.5的五个新特性:这是我看完视频以后记得最牢固的东西
1.静态导入静态导入获取绝对值:
import static java.lang.Math.*;
2.增强for循环
高级for:
for(数据类型 变量名:集合变量名){}
享元模式 flyweight 如果有很多很小的对象,他们有很多相同的东西把它们变成一个对象,
不同的属性变成外部属性作为方法的参数传入
3.枚举
1.在比较两个枚举类型的值时,不需要调用equals,直接使用“==”。
2.枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
3.所有枚举类都继承了Enum的方法。
4.values()方法:静态方法,返回一个包含全部枚举值的数组。编译时生成,非继承自Enum,故在API中找不到。
5.构造方法必须定义成私有的
6.如果有多个构造方法,该如何选择哪个构造方法?
7.枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
8.带方法的枚举
9.定义枚举TrafficLamp
10.实现普通的next方法
11.实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
12.增加上表示时间的构造方法
13.枚举只有一个成员时,就可以作为一种单例的实现方式。
14.代码实例:
根据枚举取得当前红绿灯的下一个指示灯。
例如:当前为红灯,则输出绿灯;
当前为绿灯,则输出黄灯;
当前为黄灯,则输出红灯。
//枚举类:红、绿、黄
enum TrafficLamp
{
red,green,yellow
}
class EnumTest
{
public static void main(String[] args) throws Exception
{
//代码编译之后,编译器将enum类型单独编译成了一个.class字节码文件
Class c=Class.forName("EnumTest");
EnumTest et=(EnumTest)c.newInstance();
String lamp=et.next("red");
System.out.println("下个是:"+lamp);
}
//获取下一个指示灯
public static String next(String Lamp)
{
//根据字符串取得对应枚举值
TrafficLamp tl=TrafficLamp.valueOf(Lamp);
//如果是最后一个值返回则取第1个值
if((TrafficLamp.values().length-1)==tl.ordinal())
tl=TrafficLamp.values()[0];//取第一个枚举值
else
tl=TrafficLamp.values()[tl.ordinal()+1];//取下一个值
return tl.toString();
}
}
4.自动拆装箱
几种常见的数据类型的基本类型和包装类:
byte------Byte
int-------Integer
char------Char
double----Double
float-----Float
装箱:把基本数据类型装成java类。
拆箱:把java类拆成基本数据类型(取消托管?)。
java新版本增加了自动装箱拆箱的特性,使得java基本类和基本数据类型可以无需转换直接参与计算。如:
Integer iObj = 3 //自动装箱。
system.out.println(iObj + 12);//自动拆箱。
在较早版本中不支持自动装箱拆箱,以上两句语句是错误的。
在装箱时,将基本数值类型装成java类Integer对象, 如:
Integer i1 = 13;
时如果数值在一个字节之内-128~127之间时,会先把它缓存起来,当下次又要把一个数值装成Integer对象时,会先去缓存的对象中去找如果有会直接把它拿来用不会产生一个新对象。
这样的设计是享元模式,如果会产生很多小对象,而这些小对象的基本属性又是一样的而且使用时不会变,就没必要每次都产生一个新对象,创建一个就行了。
5.泛型
只有引用类型才能作为泛型的实际参数。
定义一个方法自动将Object类型的对象转换成其他类型。
public static void main(String[] args) {
Object obj="abv";
String s=auto(obj);
}
public static <T> T auto(Object obj){
return (T)obj;
}
二、反射
概念:动态获取指定的类及动态调用类中的内容。
在java.lang.reflect包中有3个类Field,Method和Constructor类分别用于描述类的域、方法和构造器
1.Constructor类代表某个类中的一个构造方法
2.Field类代表某个类中的一个成员变量。
3.Method类代表某个类中的一个成员方法
在运行时使用反射分析对象,还可以查看在编译时还不清楚的对象域。
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
好处:大大提高了程序的扩展性。
弊端:反射机制不适于编写应用程序,因为编译器很难发现程序中的错误,任何错误只能在运行时才被发现,并导致异常。
获取构造函数:如果要通过指定的构造函数初始化对象怎么办?
思路:1.获取字节码文件对象
2.再获取给定的构造函数
3.通过构造函数初始化对象
代码如下:
//定义一个Person类
package interview;
public class Person {
private int age;
private String name;
public Person(){
super();
System.out.println("person run");
}
public Person(int age,String name){
super();
this.age=age;
this.name=name;
}
public int getAge()
{
return age;
}
public String getName(){
return name;
}
public void show(int age,String name){
System.out.println(age+","+name);
}
}
package interview;
import java.lang.reflect.Constructor;
public class getConstructor {
public static void main(String[] args)throws Exception{
String className="interview.Person";
Class clazz=Class.forName(className);
Constructor cons=clazz.getConstructor(int.class,String.class);
//访问的是公共的构造方法,如果构造方法不是public的,运行时发生NoSuchMethodException
Object obj=cons.newInstance(23,"liu");
System.out.println(obj.toString());
}
}
获取字段,(通过暴力访问)
因为成员变量是private所以需通过setAccessiable进行暴力访问。
代码如下:
package interview;
import java.lang.reflect.Field;
public class getField {
public static void main(String[] args) throws Exception{
String className="interview.Person";
Class clazz=Class.forName(className);
String fieldName="age";
Field field=clazz.getDeclaredField(fieldName);
Object obj=clazz.newInstance();
field.setAccessible(true);
field.set(obj,20);
}
获取方法
代码如下:
package interview;
import java.lang.reflect.Method;
public class getMethod {
public static void main(String[] args)throws Exception{
String className="interview.Person";
Class clazz=Class.forName(className);
Method method=clazz.getMethod("show",int.class,String.class);
Object obj=clazz.newInstance();
method.invoke(obj,20,"liu");
}
}
三、内省
1.什么是内省?内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。 Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中。
2.直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter 方法;
代码如下:
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Reflect {
public static void main(String[] args)throws Exception{
Point point=new Point(1,2);
String proName="x";
getProperty(point,proName);
setProperty(point,proName);
}
private static void setProperty(Point point,String proName)throws Exception{
PropertyDescriptor proDescriptor=new
PropertyDescriptor(proName,Point.class);
Method methodSetX=proDescriptor.getWriteMethod();
methodSetX.invoke(point,8);
System.out.println(point.getX());
}
private static void getProperty(Point point,String proName)throws Exception{
PropertyDescriptor proDescriptor=new
PropertyDescriptor(proName,Point.class);
Method methodGetX=proDescriptor.getReadMethod();
System.out.println(methodGetX.invoke(point));
}
}
public class Point {
private Integer x;
private Integer y;
public Point(Integer x,Integer y){
super();
this.x=x;
this.y=y;
}
public Integer getX(){
return x;
}
public void setX(Integer x){
this.x=x;
}
public Integer getY(){
return y;
}
public void setY(Integer y){
this.y=y;
}
}
四、JavaBean
1.什么是JavaBean呢?
JavaBean是符合某种规范的Java组件,即Java类,必须满足以下规范:
(1)必须有一个无参的默认构造函数
(2)必须有get和set方法,类的字段必须通过get和set方法来访问(get方法无参,set方法有参)
注解
1.标准注解类:
a.Override
表示一个方法声明打算重写父类中的另一个方法声明
b. SuppressWarnings
指示应该在注解元素中取消显示指定的编译器警告
c. Deprecated
不鼓励程序员使用这样的元素,因为很危险或存在更好的选择
2.代码示例
@Override
public String totring(){
//some code
}
编译时会提示错误,因为此方法并没有重写父类里的toString()方法。
@Deprecated
public String getString(){
//some code
}
当其他类试图调用或重写此方法时,编译器会发出警告
@SuppressWarnings(“unchecked”)
public void add(){
List temp = new ArrayList();
temp.add("annotation test!!!");
}
当其他类试图调用或重写此方法时,编译器会发出警告
3.自定义注解
Public @interface TestAnnotation{
int id();
String description() default “nothing”;
String[] reason();
}
使用时:1.在所有修饰符前声明;2.如果含有成员,声明时必须初始化;3.允许使用默认值;4.只有单个成员变量时,最好叫做value(),这样可以使用简化的初始声明。
对注解的注解:
(1.Target,从枚举类ElementType里取一个或多个值,指明此注解可以应用的程序元素。
(2.Retention,保持性,从枚举类RetentionPolicy里取唯一值指明编译器处理此注解的方式。
(3.Documented,注解应出现在javadoc里;RetentionPolicy.RUNTIME的注解才能使用。
(4.Inherited,表示此注解的方法期望把父类的注解也继承下来。慎用。
@Documented
@Inherited
@Target(value={ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME )
Public @interface AdvancedAnnotation{
//some members declared here.
}
4.利用反射机制我们可以对annotation进行灵活的处理
(1.获得不同的Annotation
(2.获得Annotation的成员变量
5.动态代理
一、什么是动态代理?
动态代理是为对象提供一种代理以控制对这个对象的访问。
二、什么时候使用动态代理?
当不允许直接访问某些类时或对访问的对象要做特殊处理时。
三、Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:
Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
Static Object newProxyInstance(ClassLoader loader,
Class[] interfaces, InvocationHandler h);
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。
动态代理实例:
import java.lang.reflect.*;
class ProxyDemo
{
public static void main(String[] args) throws Exception
{
UserServiceBean ub=new UserServiceBean();
UserViceProxy up=new UserViceProxy(ub);
UserService us=(UserService)Proxy.newProxyInstance(ub.getClass().
getClassLoader();
ub.getClass().getInterfaces(),up);
us.say();
}
}
interface UserService //定义一个接口
{
public void say();
}
class UserServiceBean implements UserService //实现UserService接口
{
public void say();
{
System.out.println("Hello!");
}
}
class UserViceProxy implements InvocationHandler
{
private Object obj;
UserViceProxy(Object obj)
{
this.obj=obj;
}
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable
{
return method.invoke(obj,args);
}
}
6.线程池
一简介
线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的。在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。
二:线程池
线程池的作用:
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
为什么要用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
比较重要的几个类:
ExecutorService
真正的线程池接口。
ScheduledExecutorService
能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
ThreadPoolExecutor
ExecutorService的默认实现。
ScheduledThreadPoolExecutor
继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2.newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3. newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4.newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
实例
1:newSingleThreadExecutor
代码:
package threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewSingleThreadExecutor {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个可重用固定线程数的线程池
ExecutorService pool=Executors.newSingleThreadExecutor();
//创建实现了Runnable接口对象
Thread t1=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程1");
}
};
Thread t2=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程2");
}
};
Thread t3=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程3");
}
};
Thread t4=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程4");
}
};
Thread t5=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程5");
}
};
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程
pool.shutdown();
}
}
输出结果pool-1-thread-1正在执行线程1
pool-1-thread-1正在执行线程2
pool-1-thread-1正在执行线程3
pool-1-thread-1正在执行线程4
pool-1-thread-1正在执行线程5
2.newFixedThreadPool
package threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NexFixedThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个可重用固定线程数的线程池
ExecutorService pool=Executors.newFixedThreadPool(3);
//创建实现了Runnable接口对象
Thread t1=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程1");
}
};
Thread t2=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程2");
}
};
Thread t3=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程3");
}
};
Thread t4=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程4");
}
};
Thread t5=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程5");
}
};
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程
pool.shutdown();
}
}
输出结果
pool-1-thread-3正在执行线程3
pool-1-thread-1正在执行线程1
pool-1-thread-2正在执行线程2
pool-1-thread-1正在执行线程5
pool-1-thread-3正在执行线程4
3 newCachedThreadPool
import java.util.concurrent.Executors;
public class NewCachedThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个可重用固定线程数的线程池
ExecutorService pool=Executors.newCachedThreadPool();
//创建实现了Runnable接口对象
Thread t1=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程1");
}
};
Thread t2=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程2");
}
};
Thread t3=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程3");
}
};
Thread t4=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程4");
}
};
Thread t5=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行线程5");
}
};
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程
pool.shutdown();
}
}
输出结果:
pool-1-thread-1正在执行线程1
pool-1-thread-4正在执行线程4
pool-1-thread-5正在执行线程5
pool-1-thread-3正在执行线程3
pool-1-thread-2正在执行线程2
4ScheduledThreadPool
package threadPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledThreadPoolExecutor;
public class ScheduledThreadPoolExecutorTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ScheduledThreadPoolExecutor stpe=new ScheduledThreadPoolExecutor(5);
stpe.scheduleAtFixedRate(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+"boombing");
}
},1000,1000, TimeUnit.MILLISECONDS);//1s后执行1个线程,然后以1+1=2s执行1个线程,然后1+2*1=3s执行1个线程
stpe.scheduleAtFixedRate(new Runnable(){
public void run(){
System.out.println(System.nanoTime());// 返回最准确的可用系统计时器的当前值
}
},1000,2000, TimeUnit.MILLISECONDS);
}
}
输出结果
pool-1-thread-1boombing
35676046677778
pool-1-thread-2boombing
pool-1-thread-1boombing
35678045981107
pool-1-thread-1boombing
pool-1-thread-1boombing
35680046431459
pool-1-thread-4boombing
pool-1-thread-4boombing
35682046069371
.......
7.类加载器
一、什么是类加载器?
Java和其他语言不同的是,Java是运行于Java虚拟机(JVM)。这就意味着编译后的代码是以一种和平台无关的格式保存的,而不是某种特定的机器上运行的格式。这种格式和传统的可执行代码格式有很多重要的区别。Java程序不是一个独
立的可执行文件,而是由很多分开的类文件组成,每个类文件对应一个Java类。 另外,这些类文件并不是马上加载到内存,而是当程序需要的时候才加载。 类加载器就是Java虚拟机中用来把类加载到内存的工具。而且,Java类加载器也是用Java实现的。这样就可以很容易创建自己的类加载器了。
二、为什么要创建类加载器?
既然Java虚拟金已经有了类加载器,我们还要自己创建其他的呢?
默认的类加载器只知道如何从本地系统加载类。当你的程序完全在本机编译的话,默认的类加载器一般都工作的很好。但是Java很容易的从网络上而不只是本地加载类。举个例子,浏览器可以通过自定义的类加载器加载类。 还有很多加载类的方式。除了简单的从本地或者网络外,还可以通过自定义Java中的地方之一:
* 执行非信任代码前自动验证数字签名
* 根据用户提供的密码解密代码
* 根据用户的需要动态的创建类
你关心的任何东西都能方便的以字节码的形式集成到你的应用中
三、java类加载器:
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
四、使用自定义类加载器的原因
默认的 java.lang.ClassLoader仅仅可以从加载本地文件系统的类。Java被设计成不论本地磁盘或网络都有足够的弹性加载类,并且可以在加载之前处理特殊事物。
通常默认加载器是bootstrap类加载器;它负责加载诸如java.lang.Object等关键类和加载其他rt.jar文件的运行时代码到内存。因为Java语言规范没有提供bootstrap类加载器的详细信息,不同的JVM可能有不同的类加载器。如果看到网页上有applets在运行,则它使用的是自定义类加载器。嵌入到浏览器中的applet阅读器包含了可以访问远程服务器上站点的类加载器,它可以通过HTTP加载原始字节码文件,并且在JVM中将它们转换成类。
类加载器(除了bootstrap类加载器)有父类加载器,这些父类是基本加载器的加载器实例。最重要的一点是设置正确的父加载器。然后可以使用类加载器的getParent()方法实现委派类请求(例如:自定义类加载器找不到使用专门方法的类时)。此时必须为将父加载器作为 java.lang.ClassLoader构造器的参数:
public class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
super(MyClassLoader.class.getClassLoader());
}
}
loadClass(String name)方法是ClassLoader的入口。名字参数是完全资格类名(FQCN),例如关于包类名。如果父加载器设置正确,当请求 MyClassLoader中的loadClass(String name)方法加载类,但又找不到需要加载的类时,则首先会询问父加载器。如果父加载器也找不到此类,则调用findClass(String name)方法。默认状态下findClass(String name)会抛出ClassNotFoundException例外,很多开发人员都很清楚这个例外。自定义类加载器的开发者都希望从 java.lang.ClassLoader继承时跳过这个方法。
findClass()方法的目标是为MyClassLoader容纳所有专门代码,此时不需要重复其他代码(例如当加载失败时调用系统 ClassLoader)。在此方法中,ClassLoader需要从原文件中获取字节码。一旦找到字节码则会调用defineClass()方法。 ClassLoader实例调用此方法是非常重要的。因此,如果两个ClassLoader实例定义了来自不同或相同原文件的字节码,则被定义的类也将区别对待。
我们给出两个相似的类加载器MyClassLoader1 和 MyClassLoader2,它们都可以从相同的源文件找到MyCoolClass字节码。如果一个程序通过这两个加载器分别独立加载 MyCoolClass实例(coolClass1通过MyClassLoader1加载, coolClass2通过MyClassLoader2加载),MyCoolClass.class能够被独立定义。执行下面的代码:
MyCoolClass coolClass1 = (MyCoolClass)coolClass2;
将得到一个ClassCastException例外。(开发者如果没有很好的理解类加载机制则经常碰到这样的情况。)因为它们是不同的加载器 所定义的,JVM将它们看成不同的类。虽然它们是相同类型的类并且从相同的源文件加载,但是变量coolClass1和coolClass2不兼容。
不论是否跳过findClass() 或 loadClass(),getSystemClassLoader()方法将以实际ClassLoader对象的形式直接访问系统 ClassLoader。也可以通过调用findSystemClass(String name)方法间接访问。getParent()方法允许获得父加载器。
五、类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
六、怎么定义一个类加载器?
1.自定义的类加载器继承ClassLoader
2.覆盖findClass方法
3.调用defineClass方法把得到的class文件转换成字节码。