Java基础学习第二十七天——类加载器和反射

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

加载

就是指将class文件读入内存,并为之创建一个Class对象。

任何类被使用时系统都会建立一个Class对象。

连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

初始化 就是我们以前讲过的初始化步骤

类初始化时机

创建类的实例

访问类的静态变量,或者为静态变量赋值

调用类的静态方法

使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

初始化某个类的子类

直接使用java.exe命令来运行某个主类

类加载器

负责将.class文件加载到内存中,并为之生成对应的Class对象。

虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

类加载器的组成

Bootstrap ClassLoader 根类加载器

Extension ClassLoader 扩展类加载器

Sysetm ClassLoader 系统类加载器

类加载器的作用

Bootstrap ClassLoader 根类加载器

也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

Sysetm ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。

通过反射获取构造方法并使用

获取构造方法

getConstructors

getDeclaredConstructors

创建对象

newInstance()

con.newInstance(“zhangsan”, 20);

获取所有成员

getFields,getDeclaredFields

获取单个成员

getField,getDeclaredField

修改成员的值

set(Object obj,Object value)

将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

获取所有方法

getMethods

getDeclaredMethods

获取单个方法

getMethod

getDeclaredMethod

暴力访问

method.setAccessible(true);

获取class文件对象的方式:

A:Object类的getClass()方法

B:数据类型的静态属性class

C:Class类中的静态方法

public static Class forName(String className)

一般我们使用谁呢?

A:自己玩 任选一种,第二种比较方便

B:开发 第三种

为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。

通过反射获取无参构造方法并使用

/*

* 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。

* Class类:

*      成员变量    Field

*      构造方法    Constructor

*      成员方法    Method

*

*/publicclassReflectDemo{publicstaticvoidmain(String[]args)throwsClassNotFoundException{// 方式1Person p=newPerson();Classc=p.getClass();Person p2=newPerson();Classc2=p2.getClass();System.out.println(p==p2);// falseSystem.out.println(c==c2);// true// 方式2Classc3=Person.class;// int.class;// String.class;System.out.println(c==c3);// 方式3// ClassNotFoundExceptionClassc4=Class.forName("cn.itcast_01.Person");System.out.println(c==c4);}}publicclassPerson{privateString name;intage;publicString address;publicPerson(){}privatePerson(String name){this.name=name;}Person(String name,intage){this.name=name;this.age=age;}publicPerson(String name,intage,String address){this.name=name;this.age=age;this.address=address;}publicvoidshow(){System.out.println("show");}publicvoidmethod(String s){System.out.println("method "+s);}publicStringgetString(String s,inti){returns+"---"+i;}privatevoidfunction(){System.out.println("function");}@OverridepublicStringtoString(){return"Person [name="+name+", age="+age+", address="+address+"]";}}

通过反射获取无参构造方法的使用

/*

* 通过反射获取构造方法并使用。

*/publicclassReflectDemo{publicstaticvoidmain(String[]args)throwsException{// 获取字节码文件对象Classc=Class.forName("cn.itcast_01.Person");// 获取构造方法// public Constructor[] getConstructors():所有公共构造方法// public Constructor[] getDeclaredConstructors():所有构造方法// Constructor[] cons = c.getDeclaredConstructors();// for (Constructor con : cons) {// System.out.println(con);// }// 获取单个构造方法// public Constructor getConstructor(Class... parameterTypes)// 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象Constructor con=c.getConstructor();// 返回的是构造方法对象// Person p = new Person();// System.out.println(p);// public T newInstance(Object... initargs)// 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。Object obj=con.newInstance();System.out.println(obj);// Person p = (Person)obj;// p.show();}}

获取带参构造方法

/*

* 需求:通过反射去获取该构造方法并使用:

* public Person(String name, int age, String address)

*

* Person p = new Person("林青霞",27,"北京");

* System.out.println(p);

*/publicclassReflectDemo2{publicstaticvoidmain(String[]args)throwsException{// 获取字节码文件对象Classc=Class.forName("cn.itcast_01.Person");// 获取带参构造方法对象// public Constructor getConstructor(Class... parameterTypes)Constructor con=c.getConstructor(String.class,int.class,String.class);// 通过带参构造方法对象创建对象// public T newInstance(Object... initargs)Object obj=con.newInstance("林青霞",27,"北京");System.out.println(obj);}}

获取私有构造方法并使用

/*

* 需求:通过反射获取私有构造方法并使用

* private Person(String name){}

*

* Person p = new Person("风清扬");

* System.out.println(p);

*/publicclassReflectDemo3{publicstaticvoidmain(String[]args)throwsException{// 获取字节码文件对象Classc=Class.forName("cn.itcast_01.Person");// 获取私有构造方法对象// NoSuchMethodException:每有这个方法异常// 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。Constructor con=c.getDeclaredConstructor(String.class);// 用该私有构造方法创建对象// IllegalAccessException:非法的访问异常。// 暴力访问con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。Object obj=con.newInstance("风清扬");System.out.println(obj);}}

获取成员变量并使用

/*

* 通过反射获取成员变量并使用

*/publicclassReflectDemo{publicstaticvoidmain(String[]args)throwsException{// 获取字节码文件对象Classc=Class.forName("cn.itcast_01.Person");// 获取所有的成员变量// Field[] fields = c.getFields();// Field[] fields = c.getDeclaredFields();// for (Field field : fields) {// System.out.println(field);// }/*

        * Person p = new Person(); p.address = "北京"; System.out.println(p);

        */// 通过无参构造方法创建对象Constructor con=c.getConstructor();Object obj=con.newInstance();System.out.println(obj);// 获取单个的成员变量// 获取address并对其赋值Field addressField=c.getField("address");// public void set(Object obj,Object value)// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。addressField.set(obj,"北京");// 给obj对象的addressField字段设置值为"北京"System.out.println(obj);// 获取name并对其赋值// NoSuchFieldExceptionField nameField=c.getDeclaredField("name");// IllegalAccessExceptionnameField.setAccessible(true);nameField.set(obj,"林青霞");System.out.println(obj);// 获取age并对其赋值Field ageField=c.getDeclaredField("age");ageField.setAccessible(true);ageField.set(obj,27);System.out.println(obj);}}

获取无参无返回值成员方法并使用

获取带参数返回值成员方法并使用

publicclassReflectDemo{publicstaticvoidmain(String[]args)throwsException{// 获取字节码文件对象Classc=Class.forName("cn.itcast_01.Person");// 获取所有的方法// Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法// Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法// for (Method method : methods) {// System.out.println(method);// }Constructor con=c.getConstructor();Object obj=con.newInstance();/*

        * Person p = new Person(); p.show();

        */// 获取单个方法并使用// public void show()// public Method getMethod(String name,Class... parameterTypes)// 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型Method m1=c.getMethod("show");// obj.m1(); // 错误// public Object invoke(Object obj,Object... args)// 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数m1.invoke(obj);// 调用obj对象的m1方法System.out.println("----------");// public void method(String s)Method m2=c.getMethod("method",String.class);m2.invoke(obj,"hello");System.out.println("----------");// public String getString(String s, int i)Method m3=c.getMethod("getString",String.class,int.class);Object objString=m3.invoke(obj,"hello",100);System.out.println(objString);// String s = (String)m3.invoke(obj, "hello",100);// System.out.println(s);System.out.println("----------");// private void function()Method m4=c.getDeclaredMethod("function");m4.setAccessible(true);m4.invoke(obj);}}

反射应用举例

通过配置文件运行类中的方法

publicclassStudent{publicvoidlove(){System.out.println("爱生活,爱Java");}}publicclassTeacher{publicvoidlove(){System.out.println("爱生活,爱青霞");}}publicclassWorker{publicvoidlove(){System.out.println("爱生活,爱老婆");}}/*

* 通过配置文件运行类中的方法

*

* 反射:

*      需要有配置文件配合使用。

*      用class.txt代替。

*      并且你知道有两个键。

*          className

*          methodName

*/publicclassTest{publicstaticvoidmain(String[]args)throwsException{// 反射前的做法// Student s = new Student();// s.love();// Teacher t = new Teacher();// t.love();// Worker w = new Worker();// w.love();// 反射后的做法// 加载键值对数据Properties prop=newProperties();FileReader fr=newFileReader("class.txt");prop.load(fr);fr.close();// 获取数据String className=prop.getProperty("className");String methodName=prop.getProperty("methodName");// 反射Classc=Class.forName(className);Constructor con=c.getConstructor();Object obj=con.newInstance();// 调用方法Method m=c.getMethod(methodName);m.invoke(obj);}}

我给你ArrayList的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

publicclassArrayListDemo{publicstaticvoidmain(String[]args)throwsNoSuchMethodException,SecurityException,IllegalAccessException,IllegalArgumentException,InvocationTargetException{// 创建集合对象ArrayListarray=newArrayList();// array.add("hello");// array.add(10);Classc=array.getClass();// 集合ArrayList的class文件对象Method m=c.getMethod("add",Object.class);m.invoke(array,"hello");// 调用array的add方法,传入的值是hellom.invoke(array,"world");m.invoke(array,"java");System.out.println(array);}}

写一个方法,

public void setProperty(Object obj, String propertyName, Object value){},

此方法可将obj对象中名为propertyName的属性的值设置为value。

publicclassTool{publicvoidsetProperty(Object obj,String propertyName,Object value)throwsNoSuchFieldException,SecurityException,IllegalArgumentException,IllegalAccessException{// 根据对象获取字节码文件对象Classc=obj.getClass();// 获取该对象的propertyName成员变量Field field=c.getDeclaredField(propertyName);// 取消访问检查field.setAccessible(true);// 给对象的成员变量赋值为指定的值field.set(obj,value);}}publicclassToolDemo{publicstaticvoidmain(String[]args)throwsNoSuchFieldException,SecurityException,IllegalArgumentException,IllegalAccessException{Person p=newPerson();Tool t=newTool();t.setProperty(p,"name","林青霞");t.setProperty(p,"age",27);System.out.println(p);System.out.println("-----------");Dog d=newDog();t.setProperty(d,"sex",'男');t.setProperty(d,"price",12.34f);System.out.println(d);}}classDog{charsex;floatprice;@OverridepublicStringtoString(){returnsex+"---"+price;}}classPerson{privateString name;publicintage;@OverridepublicStringtoString(){returnname+"---"+age;}}

动态代理

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

举例:春季回家买票让人代买

动态代理:在程序运行过程中产生的这个对象

而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib。

Proxy类中的方法创建动态代理类对象

publicstaticObjectnewProxyInstance(ClassLoader loader,ClasspublicclassMyInvocationHandlerimplementsInvocationHandler{privateObject target;// 目标对象publicMyInvocationHandler(Object target){this.target=target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[]args)throwsThrowable{System.out.println("权限校验");Object result=method.invoke(target,args);System.out.println("日志记录");returnresult;// 返回的是代理对象}}publicinterfaceStudentDao{publicabstractvoidlogin();publicabstractvoidregist();}publicclassStudentDaoImplimplementsStudentDao{@Overridepublicvoidlogin(){System.out.println("登录功能");}@Overridepublicvoidregist(){System.out.println("注册功能");}}/*

* 用户操作接口

*/publicinterfaceUserDao{publicabstractvoidadd();publicabstractvoiddelete();publicabstractvoidupdate();publicabstractvoidfind();}publicclassUserDaoImplimplementsUserDao{@Overridepublicvoidadd(){System.out.println("添加功能");}@Overridepublicvoiddelete(){System.out.println("删除功能");}@Overridepublicvoidupdate(){System.out.println("修改功能");}@Overridepublicvoidfind(){System.out.println("查找功能");}}publicclassTest{publicstaticvoidmain(String[]args){UserDao ud=newUserDaoImpl();ud.add();ud.delete();ud.update();ud.find();System.out.println("-----------");// 我们要创建一个动态代理对象// 我准备对ud对象做一个代理对象MyInvocationHandler handler=newMyInvocationHandler(ud);UserDao proxy=(UserDao)Proxy.newProxyInstance(ud.getClass().getClassLoader(),ud.getClass().getInterfaces(),handler);proxy.add();proxy.delete();proxy.update();proxy.find();System.out.println("-----------");StudentDao sd=newStudentDaoImpl();MyInvocationHandler handler2=newMyInvocationHandler(sd);StudentDao proxy2=(StudentDao)Proxy.newProxyInstance(sd.getClass().getClassLoader(),sd.getClass().getInterfaces(),handler2);proxy2.login();proxy2.regist();}}

模版设计模式(抽象类)

模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现

优点

使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求

缺点

如果算法骨架有修改的话,则需要修改抽象类

p

ublicclassForDemoextendsGetTime{@Overridepublicvoidcode(){for(intx=0;x<100000;x++){System.out.println(x);}}}publicabstractclassGetTime{// 需求:请给我计算出一段代码的运行时间publiclonggetTime(){longstart=System.currentTimeMillis();code();longend=System.currentTimeMillis();returnend-start;}publicabstractvoidcode();}publicclassGetTimeDemo{publicstaticvoidmain(String[]args){// GetTime gt = new GetTime();// System.out.println(gt.getTime() + "毫秒");GetTime gt=newForDemo();System.out.println(gt.getTime()+"毫秒");gt=newIODemo();System.out.println(gt.getTime()+"毫秒");}}publicclassIODemoextendsGetTime{@Overridepublicvoidcode(){try{BufferedInputStream bis=newBufferedInputStream(newFileInputStream("a.avi"));BufferedOutputStream bos=newBufferedOutputStream(newFileOutputStream("b.avi"));byte[]bys=newbyte[1024];intlen=0;while((len=bis.read(bys))!=-1){bos.write(bys,0,len);}bos.close();bis.close();}catch(IOExceptione){e.printStackTrace();}}}

装饰设计模式

装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案

优点

使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能

缺点

正因为可以随意组合,所以就可能出现一些不合理的逻辑

publicinterfacePhone{publicabstractvoidcall();}publicclassIPhoneimplementsPhone{@Overridepublicvoidcall(){System.out.println("手机可以打电话了");}}publicclassMusicPhoneDecorateextendsPhoneDecorate{publicMusicPhoneDecorate(Phone p){super(p);}@Overridepublicvoidcall(){super.call();System.out.println("手机可以听音乐");}}publicclassRingPhoneDecorateextendsPhoneDecorate{publicRingPhoneDecorate(Phone p){super(p);}@Overridepublicvoidcall(){System.out.println("手机可以听彩铃");super.call();}}publicabstractclassPhoneDecorateimplementsPhone{privatePhone p;publicPhoneDecorate(Phone p){this.p=p;}@Overridepublicvoidcall(){this.p.call();}}publicclassPhoneDemo{publicstaticvoidmain(String[]args){Phone p=newIPhone();p.call();System.out.println("------------");// 需求:我想在接电话前,听彩铃PhoneDecorate pd=newRingPhoneDecorate(p);pd.call();System.out.println("------------");// 需求:我想在接电话后,听音乐pd=newMusicPhoneDecorate(p);pd.call();System.out.println("------------");// 需求:我要想手机在接前听彩铃,接后听音乐// 自己提供装饰类,在打电话前听彩铃,打电话后听音乐pd=newRingPhoneDecorate(newMusicPhoneDecorate(p));pd.call();System.out.println("----------");// 想想我们在IO流中的使用// InputStream is = System.in;// InputStreamReader isr = new InputStreamReader(is);// BufferedReader br = new BufferedReader(isr);BufferedReader br=newBufferedReader(newInputStreamReader(System.in));BufferedWriter bw=newBufferedWriter((newOutputStreamWriter(System.out)));Scanner sc=newScanner(System.in);}}

你可能感兴趣的:(Java基础学习第二十七天——类加载器和反射)