文章目录
1.反射机制的作用
通过java语言中的反射机制可以操作(读或者修改)字节码文件。
通过反射机制可以操作代码片段(class文件)。
2.反射机制的相关类
(1)所在包:java.lang.reflect
(2)相关类:
- java.lang.Class: 代表整个字节码,代表一个类型。代表一个类型。
- java.lang.reflect.Method: 代表字节码中的方法字节码。代表类中的方法。
- java.lang.reflect.Constructor: 代表字节码中的构造方法字节码。代表类中的构造方法
- java.lang.reflect.Field: 代表字节码中的属性字节码。代表类中的成员变量。
3.获取Class的三种方式
(1)Class.forName(“完整类名”)
- 该方法是一个静态方法。
- 该方法的参数是一个字符串
- 字符串需要完整的类名(完整类名,java.lang不能省略)。
package com.sdnu;
public class ClassDemo1 {
public static void main(String[] args) {
try {
Class t1 = Class.forName("java.lang.String"); //t1代表String.class,或者说代表String类型
Class t2 = Class.forName("java.util.Date"); //t2代表Date类型
Class t3 = Class.forName("java.lang.Integer"); //t3代表Integer类型
Class t4 = Class.forName("java.lang.System"); //t4代表System类型
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(2)对象.getClass()
package com.sdnu;
public class ClassDemo2 {
public static void main(String[] args) {
String s1 = "Hello World";
Class c1 = s1.getClass();
}
}
(3) java语言中任何一种类型:包括基本数据类型,都有class属性。
package com.sdnu;
public class ClassDemo3 {
public static void main(String[] args) {
Class c1 = String.class;
Class c2 = Integer.class;
Class c3 = int.class;
}
}
4.通过反射实例化对象
ReflectDemo1.java
package com.sdnu;
public class ReflectDemo1 {
public static void main(String[] args) {
try {
Class c = Class.forName("com.sdnu.bean.User");
Object object = c.newInstance();
System.out.println(object);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
User.java
package com.sdnu.bean;
public class User {
public User() {
System.out.println("无参构造方法");
}
}
通过反射机制获取class,然后通过class来实例化对象。
newInstance这个方法会调用User这个类的无参构造方法去创建实例对象。
5.通过读属性文件实例化对象
ReflectDemo2.java
package com.sdnu;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class ReflectDemo2 {
public static void main(String[] args) {
try {
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("javareflect/src/classinfo.properties");
//创建属性类对象
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
String className = pro.getProperty("className");
Class c = Class.forName(className);
Object object = c.newInstance();
System.out.println(object);
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
clasinfo.properties
className=com.sdnu.bean.User
(使用的是上一节的User.java)
使用上面的反射机制可以使得程序变得灵活,当我们创造另一个对象的时候,我们只需要修改配置文件即可。配置文件修改如下:
className=java.util.Date
则程序运行结果为如下:
6.使用forName只让静态代码块执行
ReflectDemo3.java
package com.sdnu;
public class ReflectDemo3 {
public static void main(String[] args) {
try {
Class.forName("com.sdnu.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
MyClass.java
package com.sdnu;
public class MyClass {
static {
System.out.println("静态代码块执行了!");
}
}
7.获取类路径下的文件的绝对路径
我们先来看一下之前写的代码:
FileReader reader = new FileReader("javareflect/src/classinfo.properties");
这段代码并没有什么错误,但是可移植性较差,因为在idea中默认的当前路径是project。假如代码离开idea,换到了其他地方,不一定默认路径为project。
接下来我们介绍一种新的方法来获取当前路径。(注意,这种方法值适用于类路径下,也就是idea的src下。)
Thread.currentThread():获取当前线程对象
getContextClassLoader(): 当前线程对象的方法,获取当前线程对象的类加载器对象
getResource(): 获取资源,这是类加载器对象的方法,当前线程的类加载器对象默认从类的根路径下加载资源。
PathDemo.java
package com.sdnu;
public class PathDemo {
public static void main(String[] args) {
String path = Thread.currentThread().getContextClassLoader().
getResource("classinfo.properties").getPath();
System.out.println(path);
}
}
8.以流的形式直接返回
package com.sdnu;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PathDemo2 {
public static void main(String[] args) {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo.properties");
Properties pro = new Properties();
try {
pro.load(in);
in.close();
String className = pro.getProperty("className");
System.out.println(className);
} catch (IOException e) {
e.printStackTrace();
}
}
}
9.资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
使用以下这种方式的时候,属性配置文件xxx.properties必须放置到类路径下。
package com.sdnu;
import java.util.ResourceBundle;
public class ResourceBundleDemo {
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");
System.out.println(className);
}
}
10.类加载器
(1)什么是类加载器: 专门负责加载类的命令/工具(ClassLoader)。
(2)JDK 中自带的三个类加载器:
- 启动类加载器
- 扩展类加载器
- 应用类加载器
(3)示例
有这样一段代码:String s = "Hello World"
。在开始执行代码之前,会将所需要的类加载到JVM中,也就是将 String 类加载到JVM中。
(4)加载的顺序
- 首先通过“启动类加载器”加载
启动类加载器加载的类在JDK的路径如下:
D:\JAVA-JDK\jre\lib\rt.jar
这里面的类都是java中最核心的类,比如String类
- 启动类加载器找不到的时候,通过扩展类加载器进行加载
扩展类加载器加载的类在JDK的路径如下:
D:\JAVA-JDK\jre\lib\ext\*.jar
- 扩展类加载器找不到的时候,通过应用类加载器进行加载
应用类加载器加载的类在 classpath (系统变量)中的jar包。
(5)双亲委派机制
首先从启动类加载器加载,这个称为“父”,加载不到再从扩展类加载器加载,这个称为“母”,如果都加载不到,再考虑从应用类加载器加载。
作者:Beyong
出处:Beyong博客
github地址:https://github.com/beyong2019
本博客中未标明转载的文章归作者Beyong有,欢迎转载,但未经作者同意必须保留此段声明,且在文章明显位置给出原文连接,否则保留追究法律责任的权利。