Java基础-反射机制

1.反射机制概述

反射机制有什么用
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
反射机制的相关类在哪个包下?
java.lang.reflect.*;
java.lang.Class; //代表字节码文件,代表一个类型,代表整个类
java.lang.reflect.Method; //代表字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor; //代表字节码中的构造方法节码,代表类中的构造方法
java.lang.reflect.Field; //代表字节码中的属性字节码,代表类中的成员变量)(静态变量)

class:
public class User{
field:
	int no;
constructor:	
public User(){}
	public User(int no){this.no=no;}
method:
public setNo(int no){this.no=no;}
public getNo(){return this.no;}
}

2.获取class的三种方式

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.class实例
三种方式
第一种:Class c=Class.forName(“完整类名带包名”);
第二种:Class c=对象.getClass();
第三种:Class c=任何类型.class;

2.1.Class.forName()方式

Class.forName();

  • 1.静态方法
  • 2.方法的参数是一个字符串
  • 3.字符串需要一个完整的类名
  • 4.完整类名必需带有包名,java.lang也不能省略
try {
			Class c1=Class.forName("java.lang.String");//c1代表String.class文件,或者说c1代表string类型
			Class c2=Class.forName("java.util.Date");	//c2代表Date类型
			Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型
			Class c4=Class.forName("java.lang.System");//c4代表System类型
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

2.2.obj.getClass()方式

在这里插入图片描述
java中任何一个对象都有一个方法:getClass();

String s="abc";
		Class x=s.getClass();	//x代表String.class字节码文件,x代表String类型
		System.out.println(c1==x);	//true(==判断的是对象的内存地址)
		
		Date time=new Date();
		Class y=time.getClass();
		//true(c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件)
		System.out.println(c2==y);

注意:以上代码中c1和c2是上一种方式得到的变量

2.3…class方式

java语言中任何一种数据类型,包括基本数据类型,它都有.class属性

Class z=String.class;		//z代表String类型
Class k=Date.class;			//k代表Date类型
Class f=int.class;			//f代表int类型
Class e=double.class;		//e代表double类型
System.out.println(c1==z);	//true

3.通过反射实例化对象

获取到Class能干什么
通过Class的newInstance()方法来实例化对象
注意:newInstance()方法内部实际上调用了无参构造方法,必须保证无参构造存在才可以

public class User {
	public User(){
		System.out.println("无参构造方法");
	}
}

public class ReflectTest02 {
	public static void main(String[] args) {
		//这是不使用反射机制创建对象
		User user=new User();
		System.out.println(user);
		//下面这段代码是以反射机制的方式创建对象
		try {
			//通过反射机制,获取Class,通过Class来实例化对象
			Class c=Class.forName("reflect.User");	//c代表User对象
			//newInstance()这个方法会调用User这个类的无参数构造方法,来完成对象的创建
			//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
			Object obj=c.newInstance();
			System.out.println(obj);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}	

4.通过读属性文件实例化对象

反射机制更灵活,灵活在哪里?
验证反射机制的灵活性
java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化
非常之灵活,(符合OCP的原则,对扩展开放,对修改关闭)
后期要学习的是框架,而工作过程中,也都是使用高级框架,
这些高级框架底层实现原理,都采用了反射机制,所以反射机制是重要的,
学会了反射机制有利于你理解剖析底层的源代码
classInfo.properties的文件

className=cn.yujie.bean.User
public class ReflectTest01 {
    public static void main(String[] args) throws Exception{
        //这种方式代码就写死了,只能创建一个User类型的对象
        //User user=new User();
        //以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象
        try {
            //通过IO流读取classInfo.properties文件
            FileReader reader=new FileReader("chapter15/classInfo.properties");
            //创建属性类对象Map
            Properties pro=new Properties();    //keyvalue都是String
            //加载
            pro.load(reader);
            //关闭流
            reader.close();
            //通过key获取value
            String className=pro.getProperty("className");
            //通过反射机制实例化对象
            Class c=Class.forName(className);
            Object obj=c.newInstance();
            System.out.println(obj);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

5.只让静态代码快执行可以使用forName

public class ReflectTest02 {
    public static void main(String[] args) {
        try {
            Class.forName("cn.yujie.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class MyClass{
    //静态代码块在类加载时执行,并且只执行一次
    static{
        System.out.println("MyClass类的静态代码块执行了!");
    }
}

记住:如果你只是希望一个类的静态代码快,其他代码一律不执行
你可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行
这种方式的路径的缺点是:移植性差,在IDEA中默认的当前路径是project的根。
这个代码假设离开了IDEA,换到了其他位置,可能当前路径就不是project的根了,这时这个路径就无效了。
FileReader reader=new FileReader(“chapter15/classInfo.properties”);
接下来说一种比较通用的一种路径,即使代码换位置了,这样编写仍然是通用的
注意:使用以下通用方式的前提是:这个文件必须在类路径下
记住:凡是在src下的都是类路径下

怎么获取一个文件的绝对路径,以下讲解的这种方式是通用的,但前提是:文件需要在类路径下,才能使用这种方式。
Thread.currentThread()当前线程对象

  • getContextClassLoader()是线程对象的方法,可以获取到当前线程的类加载器对象
  • getResource()这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源【获取资源】
  • getPath()获取绝对路径
//这种方式获取文件的绝对路径是通用的。
String path=Thread.currentThread().getContextClassLoader().getResource("classInfo2.properties").getPath();
System.out.println(path);

//获取db.properties文件的绝对路径(从类的根路径下作为起点开始)
String path2=Thread.currentThread().getContextClassLoader().getResource("cn/yujie/bean/classInfo2.properties").getPath();
System.out.println(path2);

在这里插入图片描述

6.以流的形式直接返回

public class IOProPertiesTest {
    public static void main(String[] args) throws Exception{
        try {
            //获取一个文件的绝对路径
//            String path=Thread.currentThread().getContextClassLoader().getResource("classInfo2.properties").getPath();
//            FileReader reader=new FileReader(path);
            //直接以流的形式返回。
            InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
            Properties pro=new Properties();
            pro.load(reader);
            reader.close();
            //通过key获取value
            String className=pro.getProperty("className");
            System.out.println(className);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }
}

7.资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容,
使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下

public class ReSourceBundleTest {
    public static void main(String[] args) {
        //资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下,文件扩展名必须是properties
        //并且在写路径的时候,路径后面的扩展名不能写
        ResourceBundle bundle =ResourceBundle.getBundle("classInfo2");
        String className=bundle.getString("className");
        System.out.println(className);
    }
}

8.类加载器

3、关于JDK中自带的类加载器:(聊一聊,不需要掌握,知道当然最好!)
什么是类加载器?

  • 专门负责加载类的命令/工具。
  • ClassLoader
    JDK中自带了3个类加载器
  • 启动类加载器:rt.jar
  • 扩展类加载器:ext/*.jar
  • 应用类加载器:classpath

假设有这样一段代码:
String s = “abc”;

  代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,那么是怎么进行加载的呢
  首先通过“启动类加载器”加载。注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
  rt.jar中都是JDK最核心的类库
  如果通过“启动类加载器”加载不到的时候,会通过"扩展类加载器"加载
  注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext*.jar
  如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。注意:应用类加载器专门加载:classpath中的类

9.双亲委派机制

java中为了保证类加载的安全,使用了双亲委派机制
优先从启动类加载器中加载,这个称为“父” “父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止

10.总结

1.什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。

2.反射机制相关的类在哪个包下?
java.lang.reflect.*;

3.反射机制相关的主要的类?

  • java.lang.Class
  • java.lang.reflect.Method;
  • java.lang.reflect.Constructor;
  • java.lang.reflect.Field;

4.在java中获取Class的三种方式?
第一种:

  • Class c = Class.forName(“完整类名”);
    第二种:
  • Class c = 对象.getClass();
    第三种:
  • Class c = int.class;
  • Class c = String.class;

5.获取了Class之后,可以调用无参数构造方法来实例化对象

//c代表的就是日期Date类型
		Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
		Object obj = c.newInstance();

一定要注意:
newInstance()底层调用的是该类型的无参数构造方法
如果没有这个无参数构造方法会出现"实例化"异常

6.如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?

  • Class.forName(“该类的类名”);
  • 这样类就加载,类加载的时候,静态代码块执行!!!!
  • 在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。

7.关于路径问题?

String path = Thread.currentThread().getContextClassLoader()
						  .getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();	

String path = Thread.currentThread().getContextClassLoader()
						  .getResource("abc").getPath();	//必须保证src下有abc文件。

String path = Thread.currentThread().getContextClassLoader()
						  .getResource("a/db").getPath();	//必须保证src下有a目录,a目录下有db文件。	
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();	
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。

这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)但是该文件要求放在类路径下,换句话说:也就是放到src下面。src下是类的根路径。

直接以流的形式返回:

InputStream in = Thread.currentThread().getContextClassLoader()					.getResourceAsStream("com/bjpowernode/test.properties");

8.IO + Properties,怎么快速绑定属性资源文件?

//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值