Java第三阶段学习(九、类加载器、反射)

时间:2023-03-09 17:27:43
Java第三阶段学习(九、类加载器、反射)

一、类加载器

1、类的加载:

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

1.1 加载:

就是指将class文件读入内存,并为之自动创建一个Class对象(字节码对象)。

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

1.2 连接

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

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

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

1.3 初始化

  就是初始化步骤

2、类初始化时机(也就是什么时候类会初始化):

1. 创建类的实例

2. 类的静态变量,或者为静态变量赋值

3. 类的静态方法

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

5. 初始化某个类的子类

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

3、类加载器:

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

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

4、类加载器的组成

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包和类路径。

二、反射

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

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

1、class类  字节码文件类

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

 获得字节码文件的三个方法:

方式一: 通过Object类中的getObject()方法

方式二:通过   类名.class获得

方式三:通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)

package com.oracle.Demo02;

public class Demo01 {
//反射:目的就是不通过new 对象();得到所有属性和方法
public static void main(String[] args) throws ClassNotFoundException {
// 获取字节码对象的三个方式
//1.通过对象获取
Person p=new Person();
Class c=p.getClass();
System.out.println(c);
//2.通过类名获取(不管是引用类型还是基本数据类型都具备)
Class c1=Person.class;
System.out.println(c1);
//都为true 因为指向的字节码文件都是同一个
System.out.println(c==c1); //true
System.out.println(c.equals(c1)); //true
//3.通过Class类中的静态方法forName(完整的包名.类名);获取
Class c2=Class.forName("com.oracle.Demo02.Person");
System.out.println(c2);
} }

2、通过反射获得构造方法并使用

在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法。

2.1 返回一个构造方法:

public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

2.2 返回多个构造方法:

public Constructor<?>[ ] getConstructors() 获取所有的public 修饰的构造方法

public Constructor<?>[ ] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

示例代码:

自定义Person类:

package com.oracle.Demo02;

public class Person {
public String name;
private int age;
static{
System.out.println("静态代码块");
}
public Person(){
System.out.println("空参构造");
}
public Person(String name,int age){
this.name=name;
this.age=age;
System.out.println("有参构造");
}
private Person(int age,String name){
this.name=name;
this.age=age;
System.out.println("私有构造");
}
public void eat(){
System.out.println("吃饭");
}
public void work(String name){
System.out.println(name+"工作");
}
private void run(String name){
System.out.println(name+"跑步");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
} }

获得构造方法并运行:

package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo02 {

    public static void main(String[] args) throws Exception {
//反射获取空参构造方法并运行:.getConstructor();
Class c=Class.forName("com.oracle.Demo02.Person");
//获取该类中所有的公共构造方法数组
// Constructor[] con=c.getConstructors(); 本方法还需要循环数组,比较麻烦
// for(Constructor c1:con){
// System.out.println(c1);
// }
Constructor con=c.getConstructor();
//调用空参构造方法 .newInstance();
// Object obj=con.newInstance();
// System.out.println(obj);
//调用work()方法
// Person p=(Person)con.newInstance();
// p.work("张三"); } }

通过反射获取有参构造方法并运行:

package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo03 {

    public static void main(String[] args) throws Exception {
// 通过反射获取有参构造方法并运行
Class c=Class.forName("com.oracle.Demo02.Person");
Constructor con=c.getConstructor(String.class,int.class);
Object obj=con.newInstance("张胜男",16);
System.out.println(obj);
} }

快速获取空参并创建对象:

package com.oracle.Demo02;

import java.lang.reflect.Constructor;

public class Demo04 {

    public static void main(String[] args) throws Exception {
// 快速获取空参并创建对象的方式
//1.类必须有空参构造,2.构造方法必须是public
// Class c=Class.forName("com.oracle.Demo02.Person");
// Object obj=c.newInstance();
// System.out.println(obj);
//获取私有的构造方法(不推荐)
//暴力反射,破坏了程序的封装性和安全性
Class c=Class.forName("com.oracle.Demo02.Person");
//获取所有的构造方法.getDeclaredConstructors();
// Constructor[] con=c.getDeclaredConstructors();
// for(Constructor cc:con){
// System.out.println(cc);
// }
//调用私有化构造方法
Constructor con=c.getDeclaredConstructor(int.class,String.class);
con.setAccessible(true); //暴力反射的开关,为true的时候,可以得到私有的成员
Object obj=con.newInstance(18,"张三");
System.out.println(obj); } }

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

在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:

3.1 返回一个成员变量

public Field getField(String name) 获取指定的 public修饰的变量(不包含私有)

public Field getDeclaredField(String name) 获取指定的任意变量(包含私有)

3.2 返回多个成员变量

public Field [ ] getFields() 获取所有public 修饰的变量 (不包含私有)

public Field [ ] getDeclaredFields() 获取所有的 变量 (包含私有)

获取成员变量,步骤如下:

1. 获取Class对象

2. 获取构造方法

3. 通过构造方法,创建对象

4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

5. 通过方法,给指定对象的指定成员变量赋值或者获取值

public void set(Object obj, Object value)

在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值

public Object get(Object obj)

返回指定对象obj中,此 Field 对象表示的成员变量的值

package com.oracle.Demo02;

import java.lang.reflect.Field;

public class Demo05 {

    public static void main(String[] args) throws Exception {
// 通过反射获取成员变量并改值
Class c=Class.forName("com.oracle.Demo02.Person");
Object obj=c.newInstance(); //反射获得的类对象
//获取所有的公共的成员变量
Field[] fields=c.getFields();
//遍历
// for(Field f:fields){
// System.out.println(f);
// }
//通过名字获取公共的成员变量
Field field=c.getField("name");
//给成员变量赋值
field.set(obj, "张三");
System.out.println(obj);
} }

 4、通过反射获得成员方法并使用:

在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

4.1 返回获取一个方法:

public Method getMethod(String name, Class<?>... parameterTypes)

获取public 修饰的方法

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

获取任意的方法,包含私有的

参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型

4.2 返回获取多个方法:

public Method[ ] getMethods() 获取本类与父类中所有public 修饰的方法

public Method[ ] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo06 {

    public static void main(String[] args) throws Exception {
Class c=Class.forName("com.oracle.Demo02.Person");
Object obj=c.newInstance();
//获取所有公共的方法(包括从父类继承来的)
// Method[] method=c.getMethods();
// for(Method m:method){
// System.out.println(m);
// }
//获取空参成员方法并运行,如果是空参方法,只需要写方法名就可以
Method method01=c.getMethod("eat");
//调用方法
method01.invoke(obj);
//如果是空参方法,只需要写方法名就可以
System.out.println(method01);
} }

获取有参方法并运行:

package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo07 {
public static void main(String[] args) throws Exception {
//获取有参方法并运行
Class c=Class.forName("com.oracle.Demo02.Person");
Method method=c.getMethod("work", String.class);
//添加 .invoke
method.invoke(c.newInstance(), "韩凯");
}
}

5、通过反射,创建对象,调用指定的方法   重点

获取成员方法,步骤如下:

1. 获取Class对象

2. 获取构造方法

3. 通过构造方法,创建对象

4. 获取指定的方法

5. 执行找到的方法

public Object invoke(Object obj,  Object... args)

执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

package com.oracle.Demo02;

import java.lang.reflect.Method;

public class Demo07 {
public static void main(String[] args) throws Exception {
//获取有参方法并运行
Class c=Class.forName("com.oracle.Demo02.Person");
Method method=c.getMethod("work", String.class);
//添加 .invoke
method.invoke(c.newInstance(), "韩凯");
}
}

 6、通过反射,创建对象,调用指定的private 方法

获取私有成员方法,步骤如下:

1. 获取Class对象

2. 获取构造方法

3. 通过构造方法,创建对象

4. 获取指定的方法

5. 开启暴力访问

6. 执行找到的方法

public Object invoke(Object obj,  Object... args)

执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

7、擦除泛型,添加元素

将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢?

我来告诉大家,其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素

package com.oracle.Demo02;

import java.lang.reflect.Method;
import java.util.ArrayList; public class Demo08 {
//有一个ArrayList<String> list
//然后往里面添加int类型数据
public static void main(String[] args) throws Exception {
ArrayList<String> arr=new ArrayList<String>();
arr.add("aaa");
//获取集合的字节码对象
Class c=arr.getClass();
//用反射获取 add的方法,Object.calss无关泛型
Method addd=c.getMethod("add", Object.class);
//添加int类型的元素
addd.invoke(arr, 1);
for(Object obj:arr){
System.out.println(obj);
} } }

8、反射配置文件:

package com.oracle.Demo03;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties; public class Demo {
//反射配置文件
public static void main(String[] args) throws IOException, Exception {
//类不清楚,方法也不清楚
//通过配置文件实现,运行的类名和方法名以键值对的形式
//保存到properrties中,具体运行哪个类里面的方法通过该配置文件去设置
//步骤:
//1.准备配置文件,写好键值对
//2.IO读取配置文件Reader
//3.文件中的键值对存储到集合中,集合中保存的键值对就是类和方法名
//4.反射获取指定类的class文件对象
//5.class文件对象获取指定方法
//6.运行方法
// Person p=new Person();
// p.eat();
FileReader fr=new FileReader("config.properties");//读取配置好的配置文件
Properties pro=new Properties(); //创建个集合,用来存键值对的
pro.load(fr); //将文件中的键值对存到集合中
fr.close(); //记得关闭
String className=pro.getProperty("className"); //获得类名和方法名
String methodName=pro.getProperty("methodName");
Class c=Class.forName(className); //反射获取字节码文件对象
Object obj=c.newInstance(); //创建类对象
Method method=c.getMethod(methodName); //利用反射获得方法
method.invoke(obj); //执行方法
} }

自定义Person类:

package com.oracle.Demo03;

public class Person {
public void eat(){
System.out.println("人在吃饭");
}
}

自定义Student类:

package com.oracle.Demo03;

public class Student {
public void study(){
System.out.println("学生在学习");
}
}

自定义Worker类:

package com.oracle.Demo03;

public class Worker {
public void work(){
System.out.println("工人在工作");
}
}

练习题:

自定义类:

Person类

package com.oracle.Demo04;

public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public Person(){ }
public Person(String name,int age){
this.name=name;
this.age=age;
}
}

Two类

package com.oracle.Demo04;

public class Two {
private String name;
public Two(){
System.out.println(name);
}
public Two(String name) {
super();
this.name = name;
System.out.println(name);
}
public void Work(){
System.out.println("键盘敲烂,月薪过万");
}
@Override
public String toString() {
return "Two [name=" + name + "]";
} }

DamoClass自定义类:

package com.oracle.Demo04;

public class DemoClass {
public void run(){
System.out.println("welcome to oracle");
}
}

1、ArrayList<Integer> list = new ArrayList<Integer>(); //这个泛型为Integer的ArrayList中存放一个String类型的对象

//1.ArrayList<Integer> list = new ArrayList<Integer>();
//这个泛型为Integer的ArrayList中存放一个String类型的对象
public class Demo01 { public static void main(String[] args) throws Exception{
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(666);
//获取集合的字节码对象
Class c=list.getClass();
//获取集合类中的添加元素方法
Method addd=c.getDeclaredMethod("add", Object.class);
//调用方法,并添加元素
addd.invoke(list, "abc");
for(Object a:list){
System.out.println(a);
}
} }

2、用反射去创建一个对象,有2种方式,尽量用代码去体现

package com.oracle.Demo04;

import java.lang.reflect.Constructor;

public class Demo02 {
//2.用反射去创建一个对象,有2种方式,尽量用代码去体现
public static void main(String[] args) throws Exception {
//创建一个关于Two自定义类的字节码对象 要有完整的包名
Class c=Class.forName("com.oracle.Demo04.Two");
//调用.newInstance();空参构造方法
Object obj=c.newInstance();
System.out.println(obj);
//调用有参构造方法 Constructor构造器类
Constructor con=c.getConstructor(String.class);
Object obj01=con.newInstance("王五");
System.out.println(obj01);
} }

3、利用反射创建一个类对象,获取指定的方法并执行

package com.oracle.Demo04;

import java.lang.reflect.Method;

public class Demo03 {

    public static void main(String[] args) throws Exception {
// 创建一个字节码对象
Class c=Class.forName("com.oracle.Demo04.Two");
//利用反射创建一个类对象
Object obj=c.newInstance();
//通过反射获得方法名为“work”的方法
Method m=c.getMethod("Work");
//调用obj类中的m方法
m.invoke(obj);
} }

4、定义一个标准的JavaBean,名叫Person,包含属性name、age。 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置, 不使用setAge方法直接使用反射方式对age赋值。

package com.oracle.Demo04;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class Demo4 {
// 定义一个标准的JavaBean,名叫Person,包含属性name、age。
// 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置,
// 不使用setAge方法直接使用反射方式对age赋值。
public static void main(String[] args) throws Exception {
//创建字节码对象
Class c=Class.forName("com.oracle.Demo04.Person");
//获取构造方法
Constructor con=c.getConstructor(String.class,int.class);
//使用构造方法 创建类对象
Object obj=con.newInstance("张三",16);
Method m=c.getMethod("setName", String.class);
m.invoke(obj, "李四");
//通过反射与成员变量名称,获得成员变量对象
Field ageField=c.getDeclaredField("age");
//因为是私有化的成员变量,所以需要暴力访问
ageField.setAccessible(true);
//给年龄赋值
ageField.set(obj, 60);
System.out.println(obj);
} }

5、从文件中通过反射读取键值对

package com.oracle.Demo04;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties; public class Demo05 { public static void main(String[] args) throws Exception {
//从文件中读取键值对
FileReader fr=new FileReader("democlass.properties");
//创建一个集合存放键值对
Properties pro=new Properties();
pro.load(fr);
fr.close();
//通过键获取值的字符串
String name=pro.getProperty("name");
String method=pro.getProperty("method");
//创建字节码对象
Class c=Class.forName(name);
//创建类对象
Object obj=c.newInstance();
//获得方法
Method m=c.getMethod(method);
//调用方法
m.invoke(obj); } }