黑马程序员————java中类的加载、反射、动态代理、枚举

来源:互联网 发布:友盟app数据统计 编辑:程序博客网 时间:2024/06/10 01:31

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


类的加载、反射、动态代理、枚举


一.类的加载

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

1.加载:

就是指将class文件读入内存,并为之创建一个Class对象
★★ 任何类被使用时系统都会建立一个Class对象

2.连接:

验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用

3.初始化:

先是静态初始化,后是成员变量初始化


4.类的初始化时机

创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类

5.类的加载器:

负责将.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目录。
System ClassLoader 系统类加载器:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。



二.反射

1.反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


2.获取的Class对象的三种方式:


1).使用对象,调用getClass();

它是Object类中的方法。此方法是普通方法,不是静态的,要通过类的对象的引用去调用。
Student stu = new Studetn();
Class stuClass = stu.getClass();


2).静态的class属性;

任何的"数据类型"都有一个"静态的属性"--class属性,这个属性可以获取这个类的Class对象
int.class; 
Class stuClass2 = Student.class;


3).Class类forName()方法;

用Class类中的静态方法forName()可以获取某个类的Class对象;
public static Class forName(String className)
这里的classname是一个全名限定的类名,也就是带包名的。
Class stuClass3 = Class.forName("cn.itcast.demo01_获取Class对象的三种方式.Student");


   注意:在运行期间,一个类,只有一个Class对象产生。也就是说无论哪种方式获取的class对象都是同一个对象。


三:通过反射获取和使用构造方法和类中成员变量即成员方法


1.获取构造方法:使用的都是class类中的方法

批量获取:
public Constructor<?>[] getConstructors():获取所有公有的构造方法;返回一个包含某些 Constructor 对象引用的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法
范例 for(Constructor c : conArray ){
★★ System.out.println(c);}//打印出来的就是构造方法名称,如:public cn.itcast.demo02_通过Class获取某个类的的构造方法并调用.Student(boolean)
public Constructor<?>[] getDeclaredConstructors():获取所有的构造方法。包括公有,受保护,默认,私有的构造方法。

获取单个:
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取的有参数的公有的构造方法。
parameterTypes 构造方法形参的类型的Class对象。
范例:Constructor con2 = stuClass.getConstructor(boolean.class);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定的构造方法。可以是私有的。



2.调用构造方法:使用Constructor中的方法

public T newInstance(Object... initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例。
谁调用此方法就使用那个构造方法创建一个对象。也就是返回的是一个类的对象,而所传的参数就是调用构造方法所用的实际参数。
范例:obj = con2.newInstance(true);
System.out.println("obj = " + obj);//obj = cn.itcast.demo02_通过Class获取某个类的的构造方法并调用.Student@1f82982


//由于要访问是私有成员,所以这里要设置"取消访问限制检查",进行暴力访问
public void setAccessible(boolean flag),Constructor的父类AccessibleObject中的方法,取消访问检查。
可以用来访问类中的私有成员

    3.获取成员属性:class类中的方法

批量获取:
public Field[] getFields():获取所有公有的成员变量
public Field[] getDeclaredFields():获取所有的属性。包括私有的
获取单个:
public Field getField(String name):获取公有的指定属性
范例:Field f = stuClass.getField("stuName");
★★ System.out.println(f);//打印的是是属性名,而不是值。★★
public Field getDeclaredField(String name):获取指定属性。可以是私有的。


4.获取、设置属性值:Field类中的方法

public void set(Object obj,Object value):注意:设置前,一定要先实例化一个此类的对象。
参数:obj - 应该修改其字段的对象,也就是此字段所在类的对象;value - 正被修改的 obj 的字段的新值。
public object get(Object obj):获取字段的值,参数就是字段所在的类的对像,从中提取所表示字段的值的对象 。

范例:
//先实例化出一个Student对象
Object stuObj = stuClass.getConstructor().newInstance();
//为字段设置值
f.set(stuObj, "张学友");
//获取字段的值:
System.out.println("获取字段的值:" + f.get(stuObj));

对于私有的属性,要经行包里访问。
public void setAccessible(boolean flag),
Field类对象.setAccessible(true);



5.获取成员方法:class类中的方法


批量获取:
public Method[] getMethods() :获取所有的"公有"方法.并且从父类继承的方法也有★★★
public Method[] getDeclaredMethods() :获取所有方法,包括私有的。
范例:
for(Method m : methodArray){
System.out.println(m);打印的是方法名称(带参数类型)show(int)★★
获取单个:
public Method getMethod(String name, Class<?>... parameterTypes) :获取某个公有的方法。
                形参:name:方法名; parameterTypes:形参的类型列表,可变参数
public Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取某个方法。可以是私有的。


6.访问成员方法:Method类中方法

注意:调用方法前,一定要先实例化一个此类对象
public Object invoke(Object obj,Object... args);object就是使用args实参后方法的返回值,★★
参数:obj : 方法所属的对象;args: 调用方法需要的实参

同样暴力访问
public void setAccessible(boolean flag)
method对象.setAccessible(true)


四.反射的重要应用:

1.通过反射运行配置文件的内容

public class Demo {public static void main(String[] args) throws Exception{/*//获取一个class类对象,使用三种方法中那一中,class类中静态方法Class aclass =Class.forName("cn.itcast.Test反射读取配置文件.line2");//由获得Class对象再得到一个该类的一个对象Object obj=aclass.getConstructor().newInstance();//得到的该类的一个成员方法Method m=aclass.getMethod("move", int.class);m.invoke(obj, 20);*///定义一个class类对象Class aclass=Class.forName(getvalue("classname"));//通过反射,并调用构造方法,创建一个该类的对象Object obj=aclass.getConstructor(String.class).newInstance("好方法");//通过反射调用其成员方法aclass.getMethod(getvalue("methodname"), int.class).invoke(obj, 50);}//通过定义一个方法来获取几种东西public static String getvalue(String str)throws IOException{//定义一个properties集合map集合,以便于读写文件Properties pro=new Properties();//定义一个输入流FileReader filein=new FileReader(new File("pro.properties"));//读取文件中内容到集合中pro.load(filein);//关闭资源filein.close();return pro.getProperty(str);}}public class line2 {private String name;public line2(String name){this.name =name;}public void move(int n){System.out.println("名字是:"+name+"小伙的年龄是"+n);}}

2.通过泛型越过泛型检查

泛型只是在编译期间有作用,可以通过泛型直接加载其class,往声明泛型的集合添加东西

public class Demo {public static void main(String[] args) throws Exception  {//定义一个集合ArrayList<Integer> list=new ArrayList<>();list.add(10);//使用反射创建此类的class对象,调用其方法Class aclass=ArrayList.class;//list.getclass();//获取其方法的对象,使用的参数为object.class,通用的类型Method m=aclass.getMethod("add", Object.class);m.invoke(list, "nihaoma ");System.out.println(list);}}

3.通过泛型设置某个对象的某个属性为指定值

public class Demo {public static void main(String[] args) throws Exception {Cat c=new Cat();setValue(c,"name","bosimao");setValue(c, "age", 2);System.out.println("年龄:"+c.getAge()+"名字:"+c.getName());}private static void setValue(Object obj, String fieldName, Object value) throws Exception {Class aclass=obj.getClass();Field field=aclass.getDeclaredField(fieldName);//由于是私有属性,需要暴力访问field.setAccessible(true);field.set( obj, value);}}public class Cat {private String name;private int age=0;public String getName(){return this.name;}public int getAge(){return this.age;}}

五.动态代理

1.动态代理的概述

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

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

2.代理模式:

1).Student类中有一个coding()方法,写程序;
2).测试类中如果需要coding()方法,需要直接实例化一个Student,并调用方法;
如果想在coding()方法的前面或者后面添加一些其他功能,可以不用修改Student类,而为Student类添加
一个"代理类",代理类会调用Student类中的方法。
3).增加了代理类后,测试类不需要直接面对Student,转而使用代理类。代理类中为coding方法添加了新的功能。
实际上就是不直接使用基础类,而是使用第三方代理,并且代理可以添加一些基本功能
public class Demo {//测试类public static void main(String[] args) {/*Student stu = new Student();stu.coding();*///使用代理模式StudentProxy proxy = new StudentProxy();proxy.coding();}}public class StudentProxy {//代理类public void coding(){check();new Student().coding();zj();}//先期检查public void check(){System.out.println("先期检查......");}//后期总结public void zj(){System.out.println("后期总结.......");}}public class Student {//基础类public void coding(){System.out.println("做项目,写程序......");}}

3.动态代理的步骤;

1).自定义一个类,实现InvocationHander接口,并重写接口中的invoke()方法
2).在需要使用代理类的时候,使用Proxy类中的newProxyInstance()方法获取代理类的对象
3).JDK只能为接口做动态代理,所以要为所要代理的类定义一个接口,并去实现接口
public class Demo {//测试类public static void main(String[] args) throws Exception {//多态的子类对象,使用proxy类中的方法newproxyInstace//Idao idaostu=(Idao) Proxy.newProxyInstance(Class.forName("cn.itcast.Test动态代理.Student").getClassLoader(), //Student.class.getInterfaces(),new MyInvocationhander(new Student()));Idao idaostu=(Idao) Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(),new MyInvocationhander(new Student()));idaostu.write();//需要使用的方法}}public class MyInvocationhander implements InvocationHandler {//实现接口,代理类private Object obj;//定义需要代理的对象public MyInvocationhander(Object obj) {this.obj=obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object objmethod=method.invoke(obj);show();return objmethod;}private void show() {System.out.println("真是牛叉啊!!");}}public interface Idao {//自己定义接口public void write();}public class Student implements Idao {//自定义类,需要代理的类@Overridepublic void write() {System.out.println("你好,动态代理!!");}}


六.枚举

1.枚举概述

一种模式"多例模式":在整个应用程序运行期间,有些类的实例,只允许有固定的几个,这种模式就是"多例模式"
  例如:骰子:需要2个实例;
          扑克:54个实例;        
    现在说的枚举,就是基于"多例模式":  
例如:我们的程序运行期间,需要三个颜色(红\绿\蓝),所以我们使用一个类MyColor来表示颜色。因为我们只需要三个颜色,所以这个类的对象,只能有三个;

public abstract class MyColor3 {//抽象类也是可以定义多例模式public static final MyColor3 RED  = new MyColor3("红"){@Overridevoid show() {System.out.println("我是红色的!");}};public static final MyColor3 GREEN = new MyColor3("绿"){@Overridevoid show() {System.out.println("我是绿色的!");}};public static final MyColor3 BLUE = new MyColor3("蓝"){@Overridevoid show() {System.out.println("我是蓝色的!");}};private String colorName;private MyColor3(String colorName){this.colorName = colorName;}public String toString(){return this.colorName;}abstract void show();}

2.制作枚举步骤:

1).定义枚举:public enum Xxxxx
2).直接定义枚举项。注意:可以有其它成员,但枚举项必须在第一行;
3).枚举中可以包含抽象方法;
4).任何的枚举类,都继承自:Enum类;
public enum MyColor3 {RED("红") {@Overridevoid show() {System.out.println("我是红色的!");}},GREEN("绿") {@Overridevoid show() {System.out.println("我是绿色的!");}},BLUE("蓝") {@Overridevoid show() {System.out.println("我是蓝色的!");}};private String colorName;private MyColor3(String n){this.colorName = n;}public String getColorName(){return this.colorName;}abstract void show();}

3.枚举类中常用方法,使用枚举对象调用

int compareTo(E o)//比较的是枚举项的索引值,做减法
String name()//枚举项的名字fieldname
int ordinal()//枚举项的索引值
String toString()//枚举对象所对对应的fieldname
<T> T valueOf(Class<T> type,String name)//将一个字符串转换为type类型的对象
values() 
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

public class Demo {public static void main(String[] args) {MyColor3 c1 = MyColor3.RED;MyColor3 c2 = MyColor3.BLUE;System.out.println(c2.compareTo(c1));//索引值的减法System.out.println("name = " + c2.name());//BLUE//int ordinal()System.out.println("c2.ordinal() : " + c2.ordinal());System.out.println("c1.ordinal() : " + c1.ordinal());//String toString()System.out.println("c2.toString():" + c2.toString());//<T> T valueOf(Class<T> type,String name)MyColor3 c3 = c1.valueOf("RED");//将一个字符串转换为MyColor3对象MyColor3 c4 = MyColor3.valueOf(MyColor3.class,"BLUE");System.out.println(c3);System.out.println(c4);//values() MyColor3[] result = c1.values();System.out.println("循环遍历:");for(MyColor3 c : result){System.out.println(c);}}}



1 0
原创粉丝点击