文章目录
- 一、java反射机制概述
- 二、理解Class类并获取Class实例(重要)
- 三、类的加载与ClassLoader的理解
- 四、创建运行时类的对象(重要)
- 五、获取运行时类的完整结构
- 六、调用运行时类的指定结构(重要)(开发时做)
- 七、反射的应用:动态代理
尚硅谷代码视频:day11_反射
学习时长:1天
一、java反射机制概述
学习思路(B站尚硅谷)
反射之前,类的实例化
使用反射,实现同上的操作
反射的强大之处,调用类的私有结构
反射和封装性的对比
两个问题
//疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
//建议:直接new的方式。
//什么时候会使用:反射的方式。 反射的特征:动态性(登录或者注册)
//疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
//不矛盾。封装性(私有的用不着,可能是被公有的调用)
//封装性:建议是否调用。反射:能否调用。
二、理解Class类并获取Class实例(重要)
二、理解Class类并获取Class实例(重要)
(正式认识反射)(反射难理解:在于动态性)
Class类的理解
1、加载到内存中的类,我们就称为运行时类,就作为Class的一个实例(加载到内存中的类本身,加上.calass属性,充当大Class的实例)
以前通过类去造对象,现在,类本身也是对象,大Class的一个对象。
万事万物皆对象?对象.xxx,File,URL,反射,前端(一对一对的标签)、数据库操作(一条一条的记录)
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类
获取Class实例的4种方式
推荐使用第3种,如下
调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName(“com.atguigu.java.Person”);
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
方式三:调用Class的静态方法:forName(String classPath)
使用最多,更好的体现了动态性
Class clazz3 = Class.forName("com.atguigu.java.Person");
// clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);
//Class的实例就对应着一个运行时类,加载到内存中会缓存一段时间,以上只是通过不同的方式获取到
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1 == clazz3);//true
哪些类型可以有Class对象?
外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类
interface接口、[]数组、enum枚举、annotation注解@interface
primitive type基本数据类型
三、类的加载与ClassLoader的理解
理解类的加载过程、ClassLoader的理解
类的加载
类加载器
使用ClassLoader加载配置文件
Properties:用来读取配置文件
@Test
public void test2() throws Exception {
Properties pros = new Properties();
//此时的文件默认在当前的module下。
//读取配置文件的方式一:
FileInputStream fis = new FileInputStream("jdbc.properties");
// FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
pros.load(fis);
//读取配置文件的方式二:使用ClassLoader
//配置文件默认识别为:当前module的src下
// ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
// InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
// pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user = " + user + ",password = " + password);
}
四、创建运行时类的对象(重要)
四、创建运行时类的对象(重要)
有了大Class实例后,可以干什么事呢?
第1件事:通过反射创建对应的运行时类的对象
newInstance():调用此方法,创建对应的运行时类(这里为Person类)的对象。内部调用了运行时类的空参的构造器
99%情况下这么使用(.newInstance()就可以)
调用运行时类中的指定的构造器,只在特定情况下使用。
Class clazz = Person.class;
Person obj = clazz.newInstance();
//Class是一个源头,必须先要有Class
//类的泛型决定了newInstance()方法的返回值,省略了做强转
Class<Person> clazz = Person.class;
//造的就是Person类对象
/newInstance():调用此方法,创建对应的运行时类(这里为Person类)的对象。内部调用了运行时类的空参的构造器。
Person obj = clazz.newInstance();
System.out.println(obj);
体会反射的动态性
int num = new Random().nextInt(3);//0,1,2
String classPath = "";
switch(num){
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.atguigu.java.Person";
break;
}
Class clazz = Class.forName(classPath);
System.out.println(clazz.newInstance());
5次输出结果:
Sun Feb 28 11:50:59 CST 2021
java.lang.Object@573fd745
Person()
Person{name='null', age=0}
Person()
Person{name='null', age=0}
Person()
Person{name='null', age=0}
五、获取运行时类的完整结构
五、获取运行时类的完整结构
有了Class的实例,比如c1,就指向了方法区中类的实体本身
实体类中有构造器、属性、方法;也有类所在的包、实现的接口、父类;方法可以有注解、权限修饰符、返回值类型、方法名、异常等
通过反射可以拿到以上结构
不要求掌握,了解就可以,实际开发不会这么做。感受反射的强大。
获取当前运行时类的属性结构
getFields():获取当前运行时类及其父类中声明为public访问权限的属性
getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
@Test
public void test1(){
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Field f : fields){
System.out.println(f);
}
System.out.println();
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
System.out.println(f);
}
}
输出结果:
public int com.atguigu.java1.Person.id
public double com.atguigu.java1.Creature.weight
private java.lang.String com.atguigu.java1.Person.name
int com.atguigu.java1.Person.age
public int com.atguigu.java1.Person.id
获取运行时类的方法结构
getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
还可以获取:
@Xxxx
权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) throws XxxException{}
获取运行时类的构造器
getConstructors():获取当前运行时类中声明为public的构造器
getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
六、调用运行时类的指定结构(重要)(开发时做)
六、调用运行时类的指定结构(重要)(开发时做)
调用运行时类的指定属性、指定方法、指定构造器
1.获取指定的某个方法
getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod(“show”, String.class);
注解练习中使用:Method targetMethod = Cls.getDeclaredMethod(“method”);
2.保证当前方法是可访问的
show.setAccessible(true);
3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值
show.invoke(p,“CHN”); //我的国籍是:CHN
/*
如何操作运行时类中的指定方法 -- 需要掌握
*/
@Test
public void testMethod() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
/*
1.获取指定的某个方法
getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show.setAccessible(true);
/*
3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值。
*/
//invoke调用的意思
//这里常用,需要熟悉
Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); //我的国籍是:CHN
System.out.println(returnValue);//CHN
System.out.println("*************如何调用静态方法*****************");
// private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
// Object returnVal = showDesc.invoke(null); //细节:静态方法,每个对象调用都一样,所以这里可以随意写
Object returnVal = showDesc.invoke(Person.class);//我是一个可爱的人
System.out.println(returnVal);//null
}
七、反射的应用:动态代理
以上!!!
后续再总结!!!
总结(终版)
反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
属性class作为Class的实例
Class clazz = Person.class;
得到构造器
Constructor cons = clazz.getConstructor(String.class, int.class);
通过构造器造对象
Object obj = cons.newInstance("TOM", 12);
Person p = (Person) obj;
System.out.println(p.toString());
输出:Person{name='TOM', age=12}
在这里插入代码片
/*
配置文件:
放数据库配置信息,jdbc即数据库驱动
(专门用于连接数据库:主机、端口号、用户名、密码)(即hostName,userName,port,password)
第三方服务的账号信息 如:appKey , 秘钥
*/
//AM 11:08讲配置文件
新建一个配置文件:对应package下--->new--->Resource Bundle就可以
因为通过创建Properties对象加载配置信息(为什么配置文件以properties结尾?)
获取Class对象方法1
这就拿到数据库中的配置信息,可以用jdbc连接数据库
为什么把这些信息放到文件中? 方便修改:只修改一个地方,很方便
为什么讲配置文件? 为了说明第一种获取Class对象的方式是比较好的
获取Person当中的构造方法
//AM 11:35
//建一个Person类
//成员变量、构造方法、成员方法分别设置了不同的访问权限
//用反射试一下
此处省略Person类的定义
/*
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
*/
package com.cskaoyan.constructer;
import java.lang.reflect.Constructor;
/*
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
// 获取字节码文件对象personCls
//Class 类的实例表示正在运行的 Java 应用程序中的类和接口
//建议copy reference com.cskaoyan.domain.Person
Class personCls = Class.forName("com.cskaoyan.domain.Person");
// 获取构造方法
// getConstructors() 返回一个包含某些 Constructor 对象的数组,
// 这些对象反映此 Class 对象所表示的类的所有 公共 构造方法。
Constructor[] constructors = personCls.getConstructors();
System.out.println("获取所有public的构造方法------------");
for (Constructor c : constructors) {
System.out.println(c);
}
System.out.println("获取所有构造方法-------------");
// getDeclaredConstructors() 返回 Constructor 对象的一个数组,
// 这些对象反映此 Class 对象表示的类声明的所有构造方法。
Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor c : declaredConstructors) {
System.out.println(c);
}
}
}
获取Person当中的成员变量
/*
Field[] getFields()
Field[] getDeclaredFields()
*/
获取Person当中的成员方法
// 获取所有成员方法 Method 用来描述我们的成员方法
// Method[] getMethods()
//Method[] getDeclaredMethods()
获取字节码文件对象
Class personCls = Class.forName("com.cskaoyan.domain.Person");
System.out.println("获取所有成员方法----------");
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
获取所有成员方法----------
private void com.cskaoyan.domain.Person.eat(java.lang.String)
public void com.cskaoyan.domain.Person.eat()
System.out.println("获取单个private的成员方法--------");
Method eatMethod2 = personCls.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod2);
获取单个private的成员方法--------
private void com.cskaoyan.domain.Person.eat(java.lang.String)