博客 |
---|
Java-(高级) |
文章目录
类加载器
在写反射时,先介绍类加载器,因为反射都是在操作运行时Class文件对象,一个java文件,首先要编译成class文件,在使用类加载器加载到内存中。
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
1.就是指将class文件读入内存,并为之创建一个Class对象。
2.任何类被使用时系统都会建立一个Class对象。
连接
1.验证 是否有正确的内部结构,并和其他类协调一致
2.准备 负责为类的静态成员分配内存,并设置默认初始化值
3.解析 将类的二进制数据中的符号引用替换为直接引用
初始化 就是我们以前讲过的初始化步骤
1.类加载器图解
2.类加载器的组成
1)BootStrap:引导类加载器:加载都是最基础的文件
也被称为引导类加载器,负责Java核心类的加载。
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。
2)ExtClassLoader:扩展类加载器:加载都是基础的文件
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录。
3)AppClassLoader:应用类加载器:三方jar包和自己编写java文件
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
3.类初始化时机
1.创建类的实例
2.访问类的静态变量,或者为静态变量赋值
3.调用类的静态方法
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5.初始化某个类的子类
6.直接使用java.exe命令来运行某个主类
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
Class类:
成员变量 Field
构造方法 Constructor
成员方法 Method
反射-获取class文件对象的方式
获取class文件对象的方式:
A:Object类的getClass()方法
B:数据类型的静态属性class
C:Class类中的静态方法
public static Class forName(String className)
一般我们到底使用谁呢?
A:自己玩 任选一种,第二种比较方便
B:开发 第三种
为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
代码演示
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//A:Object类的getClass()方法
Person p = new Person();
Class c = p.getClass();
Person p2 = new Person();
Class c1 = p2.getClass();
System.out.println(p==p2);//false
System.out.println(c==c1);//true
//B:数据类型的静态属性class
Class c2 = Person.class;
System.out.println(c1==c2);//true
//C:Class类中的静态方法
Class c3 = Class.forName("com.ginger.demo01.Person");
System.out.println(c2==c3);//true
}
}
结果
false
true
true
true
反射-通过反射获取构造方法对象相关方法
Class类中的方法:
Constructor[] getConstructors():获取公共构造方法对象
Constructor<?>[] getDeclaredConstructors():获取所有构造方法对象(包括私有)
Constructor getConstructor(Class<?>… parameterTypes):获取单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>… parameterTypes): 获取单个公共构造方法对象(包括私有)
Constructor类中的方法:
public void setAccessible(boolean flag):值为true,取消 Java 语言访问检查。
T newInstance(Object… initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
反射获取构造方法共用的Person类
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method" + s);
}
public String getString(String s, int i) {
return s + "--" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射-通过反射获取无参构造方法对象
代码演示
public class RefletcDemo {
public static void main(String[] args) throws Exception {
//获取class文件对象
Class c = Class.forName("com.ginger.demo02.Person");
//Constructor[] getConstructors():获取公共构造方法
Constructor[] consArr1 = c.getConstructors();
for (Constructor cons1 : consArr1) {
System.out.println(cons1);
}
System.out.println("----------------------------------------------");
//Constructor<?>[] getDeclaredConstructors():返回所有构造方法包括私有
Constructor[] consArr2 = c.getDeclaredConstructors();
for (Constructor cons2 : consArr2) {
System.out.println(cons2);
}
System.out.println("----------------------------------------------");
//获取无参构造方法
//Constructor<T> getConstructor(Class<?>... parameterTypes):获取单个构造
//参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
Constructor cons3 = c.getConstructor();//返回构造方法对象
//T newInstance(Object... initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
Object o = cons3.newInstance();//等价于//Person p = new Person();
System.out.println(o);
Person p2 = (Person) o;
p2.show();
}
}
结果:
public com.ginger.demo02.Person(java.lang.String,int,java.lang.String)
public com.ginger.demo02.Person(java.lang.String,int)
public com.ginger.demo02.Person()
----------------------------------------------
public com.ginger.demo02.Person(java.lang.String,int,java.lang.String)
public com.ginger.demo02.Person(java.lang.String,int)
private com.ginger.demo02.Person(java.lang.String)
public com.ginger.demo02.Person()
----------------------------------------------
Person{name='null', age=0, address='null'}
show
反射-通过反射获取带三个参数构造方法对象
需求:通过反射去获取该构造方法并使用:
public Person(String name, int age, String address)
Person p = new Person(“亚索”,33,“艾欧尼亚”);
System.out.println§;
代码演示
public class RefletcDemo {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c1 = Class.forName("com.ginger.demo03.Person");
//获取带参构造方法对象
//Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor cons = c1.getConstructor(String.class, int.class, String.class);
//通过带参的构造方法对象,创建对象。
//T newInstance(Object... initargs)
Object o = cons.newInstance("亚索", 33, "艾欧尼亚");//等价于Person p = new Person("亚索",33,"艾欧尼亚");
System.out.println(o);
Person p = (Person) o;
p.show();
}
}
结果:
Person{name='亚索', age=33, address='艾欧尼亚'}
show
反射-通过反射获取私有构造方法对象
需求:通过反射获取私有构造方法并使用
private Person(String name)
Person p = new Person(“影流之主”);
System.out.println§;
代码演示
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c1 = Class.forName("com.ginger.demo04.Person");
//获取私有带参构造方法对象
//NoSuchMethodException:没有这样的方法
//Constructor cons = c1.getConstructor(String.class);//只能获取公共方法,所以就报错了。
Constructor cons = c1.getDeclaredConstructor(String.class);//可以获取私有构造方法
//public void setAccessible(boolean flag):值为true,取消 Java 语言访问检查。
//设置为true就可以访问私有构造方法
cons.setAccessible(true);
//IllegalAccessException:非法异常
Object o = cons.newInstance("影流之主");//因为是私有方法不能访问所以报错了。
System.out.println(o);
Person p = (Person)o;
p.show();
}
}
结果:
Person{name='影流之主', age=0, address='null'}
show
反射-通过反射获取成员变量对象相关方法
Class类中的方法:
Field[] getFields():获取公共成员变量对象
Field[] getDeclaredFields():获取所有成员变量对象(包括私有)
Field getField(String name):获取单个公共成员变量
Field getDeclaredField(String name):获取单个成员变量对象(包括私有)
Field类中的方法:
void set(Object obj,Object value):将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
void setAccessible(boolean flag):值为true,取消 Java 语言访问检查。
反射获取构造方法共用的Person类
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method" + s);
}
public String getString(String s, int i) {
return s + "--" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射-通过反射获取不同修饰符的成员变量对象
代码演示
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c1 = Class.forName("com.ginger.demo05.Person");
//获取多个成员变量对象
//Field[] getFields():获取公共成员变量对象
//Field[] fields1 = c1.getFields();//获取公共成员变量对象
//for (Field field : fields1) {
// System.out.println(field);
//}
//System.out.println("---------------------------------------");
//Field[] getDeclaredFields():获取所有成员变量对象(包括私有)
//Field[] field2 = c1.getDeclaredFields();//获取所有成员变量对象
//for (Field field : field2) {
// System.out.println(field);
//}
//System.out.println("---------------------------------------");
/*
Person p = new Person(); p.address="艾欧尼亚"; System.out.println(p);
*/
//通过无参构造创建对象
Constructor cons = c1.getConstructor();
Object obj = cons.newInstance();
//获取单个成员变量对象
//获取address字段并赋值
//Field getField(String name):获取单个公共成员变量
Field field3 = c1.getField("address");
// public void set(Object obj,Object value):将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
field3.set(obj, "艾欧尼亚");//把obj对象的addressField字段设置为"艾欧尼亚"。
System.out.println(obj);
System.out.println("---------------------------------------");
//获取name字段并赋值
//NoSuchFieldException
//Field field4 = c1.getField("name");
//Field getDeclaredField(String name):获取单个成员变量对象(包括私有)
Field field4 = c1.getDeclaredField("name");//获取单个私有成员变
//void setAccessible(boolean flag):值为true,取消 Java 语言访问检查。
field4.setAccessible(true);
field4.set(obj, "影流之主");
System.out.println(obj);
System.out.println("---------------------------------------");
//获取age字段并赋值
//NoSuchFieldException
//Field file5 = c1.getField("age");
Field file6 = c1.getDeclaredField("age");
file6.set(obj,33);
System.out.println(obj);
}
}
结果:
Person{name='null', age=0, address='艾欧尼亚'}
---------------------------------------
Person{name='影流之主', age=0, address='艾欧尼亚'}
---------------------------------------
Person{name='影流之主', age=33, address='艾欧尼亚'}
反射-通过反射获取成员方法对象相关方法
Class类中的方法:
Method[] getMethods():获取公共成员方法对象(包括从父类继承的公共方法)
Method[] getDeclaredMethods():获取自己所有成员方法对象(包括私有)
Method getMethod(String name, Class<?>… parameterTypes):获取单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>… parameterTypes):获取单个公共成员方法对象(包括私有)
Method类中的方法:
Object invoke(Object obj, Object… args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
void setAccessible(boolean flag):值为true,取消 Java 语言访问检查。
反射获取构造方法共用的Person类
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method" + s);
}
public String getString(String s, int i) {
return s + "--" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
反射-通过反射获取不同修饰符的成员方法对象
代码演示
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//创建class对象
Class c1 = Class.forName("com.ginger.demo06.Person");
//Method[] getMethods():获取公共成员方法对象(包括从父类继承的公共方法)
/*Method[] methods1 = c1.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("------------------------------------");*/
//Method[] getDeclaredMethods():获取自己所有成员方法对象(包括私有)
/*Method[] methods2 = c1.getDeclaredMethods();
for(Method method:methods2){
System.out.println(method);
}
System.out.println("------------------------------------");*/
//通过无参创建对象
Constructor cons = c1.getConstructor();
Object obj = cons.newInstance();
//获取单个成员方法对象
//Method getMethod(String name, Class<?>... parameterTypes):获取单个公共成员方法对象
//第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
Method m1 = c1.getMethod("show");
//执行方法
// obj.m1(); 错误
//第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
//invoke()方法返回值是,obj对象调用方法(这里是show)的返回值。show()方法没有返回值,这里返回是null。
Object o = m1.invoke(obj);//调用obj对象的m1方法
System.out.println(o);//null
System.out.println("------------------------------------");
Method m2 = c1.getMethod("method", String.class);
m2.invoke(obj, "-影流之主");
System.out.println("------------------------------------");
Method m3 = c1.getMethod("getString", String.class, int.class);
Object s = m3.invoke(obj, "格雷福斯-", 45);
System.out.println(s);
System.out.println("------------------------------------");
//NoSuchMethodException:未发现该方法
//Method m4 = c1.getMethod("function");
//Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取单个公共成员方法对象(包括私有)
Method m4 = c1.getDeclaredMethod("function");
//IllegalAccessException:非法的访问异常
m4.setAccessible(true);//取消 Java 语言访问检查。
m4.invoke(obj);
}
}
结果:
show
null
------------------------------------
method-影流之主
------------------------------------
格雷福斯---45
------------------------------------
function
反射练习-通过配置文件运行类中的方法
class.txt文件内容:
通过修改className的值,实现了我不需要修改代码,只用修改配置文件就可以实现调用不同类的work方法。
className=com.ginger.demo01.Teacher
method=work
Student类
public class Studnet {
public void work(){
System.out.println("学生要学习");
}
}
Teacher类
public class Teacher {
public void work(){
System.out.println("老师要教书");
}
}
测试
public class ReflectTest {
public static void main(String[] args) throws Exception {
//没使用反射前
//如果我有一个需求,随着实际的变化我需要老师、学生、工程师、或者医生类,每个类中都有一个work
//方法,不同的对象做不同的工作。
//Student s = new Student(); 某个时间我又不要学生对象
//work();
//Teacher t = new Teacher();
//work();
//Doctor d = new Doctor();
//work();
//或者某个时间我需要工程师对象,这时候就得要改代码,一般情况下代码是不能改的。可能会牵扯很多的代码。
//使用反射后
Properties p = new Properties();
//读取数据
p.load(new FileReader("class.txt"));
//通过建获取值
String className = p.getProperty("className");
String method = p.getProperty("method");
//获取class文件对象
Class c1 = Class.forName(className);
//通过无参构造创建对象
Constructor cons = c1.getConstructor();
Object obj = cons.newInstance();
//获取方法对象
Method m1 = c1.getMethod("work");
m1.invoke(obj);
}
}
结果:
老师要教书
反射练习-ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
首先要明白的是集合的泛型,其实是编译器器看的,在java文件编译成class文件的时候,是根本没有泛型的,这个问题就是绕过编译器就可以实现。
通过反编译查看创建的一个ArrayList集合,根本没有泛型。
public class ArrayListDemo{
public ArrayListDemo(){
}
public static void main(String args[]){
ArrayList list = new ArrayList();
}
}
代码实现
public class ArrayListDemo {
public static void main(String[] args) throws Exception {
//创建集合对象
ArrayList<Integer> list = new ArrayList<>();
//其实就是通过反射获取到list集合字节码文件对象,在使用add方法添加一个字符串即可。
Class c1 = list.getClass();
//获取list集合的add方法对象
Method m1 = c1.getMethod("add", Object.class);
//执行方法
m1.invoke(list,"hello");
System.out.println(list);
}
}
结果:
[hello]
反射练习-public void setProperty(Object obj, String propertyName, Object value){}此方法可将obj对象中名为propertyName的属性的值设置为value。
代码演示
public class ToolDemo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Student s = new Student();
Tool.setProperty(s, "name", "疾风剑豪");
System.out.println(s);
Tool.setProperty(s, "age", 35);
System.out.println(s);
}
}
class Student {
private String name;
public int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Tool工具类
public class Tool {
public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
//获取class文件对象
Class c1 = obj.getClass();
//获取字段对象
//防止是私有成员
Field field = c1.getDeclaredField(propertyName);
//取消Java的访问检查
field.setAccessible(true);
//设置obj对象属性值
field.set(obj,value);
}
}
结果:
Student{name='疾风剑豪', age=0}
Student{name='疾风剑豪', age=35}