java中反射-字节码和类加载器
多态的一个表现
子类类型赋值给父类 Father f1 = New Son()
调用子类方法报错。 调用父类方法OK。这个就是多态
一个对象能用什么方法,并不是取决于 它有什么方法。
而是取决于引用变量的类型(也就是取决于它声明的类型,Father类型)
它能够用的方法,一定是Father中的方法。
通过反射来获取整体的对象。这个整体的对象我们称之为:类对象。
怎么获取这个类对象呢? 通过f1.getClass()来获取类对象
字节码对象
package part;
public class Java01 {
public static void main(String[] args) {
User u1 = new User();
u1.test1();
// 通过 u1.getClass() 来获取:类对象
// Class 是java.lang包下的。所以不需要再导入了。
// 这里的aClass对象就是java中的字节码文件
// 所谓的字节码文件:就是我们使用javac编译后的那个源码文件
Class<? extends User> aClass = u1.getClass();
}
}
class User{
public void test1(){
System.out.println("test1");
}
}
class Child extends User{
public void test2(){
System.out.println("test2");
}
}
获取类中的属性,方法以及其他信息
package part;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Java01 {
public static void main(String[] args) {
User u1 = new User();
// 通过 u1.getClass() 来获取:类对象
// Class 是java.lang包下的。所以不需要再导入了。
// 这里的aClass对象就是java中的字节码文件
// 所谓的字节码文件:就是我们使用javac编译后的那个源码文件
// 类对象
Class<? extends User> aClass = u1.getClass();
// 获取类的完整名称,包含包名 输出:part.User
System.out.println(aClass.getName());
// 获取类的名称 输出:User
System.out.println(aClass.getSimpleName());
// 获取类的包的信息 输出:package part
System.out.println(aClass.getPackage());
// 获取类的包名 输出:part
System.out.println(aClass.getPackage().getName());
Class<?> superclass = aClass.getSuperclass();
// 获取类的父类 输出:class java.lang.Object
System.out.println(superclass);
// 获取类的接口,可能存在多个,因此是一个数组的哈。
Class<?>[] interfaces = aClass.getInterfaces();
// 输出0,因为我们这里没有实现接口的哈
System.out.println(interfaces.length);
try {
// 获取类的属性,只能获取公共(public)的属性
Field f1 = aClass.getField("xx");
// 获取类的属性,所有的权限,就是你是私有的,我也可以获取
Field f2 = aClass.getDeclaredField("aa");
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
// 获取类的所有属性,前提是这些属性都是public修饰的,是公共的,返回来的是数组
Field[] fields = aClass.getFields();
// 获取类的所有属性,就是你是私有的,我也可以获取,,返回来的是数组
Field[] declaredFields = aClass.getDeclaredFields();
try {
// 获取类中的公共的方法
Method method= aClass.getMethod("test");
// 获取类中的方法,就算是私有的也可以获取
Method methods = aClass.getDeclaredMethod("test2");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// 获取类中所有公共的方法,是公共的,返回来的是数组
Method[] methods = aClass.getMethods();
// 获取类中的所有方法,方法就算是私有的也可以获取,返回来的是数组
Method[] allMethods = aClass.getDeclaredMethods();
try {
// 构造方法 constructor 就是构造对象
Constructor<? extends User> constructor = aClass.getConstructor();
// 返回所有的构造方法
Constructor<?>[] constructors = aClass.getConstructors();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
class User{
public void test1(){
System.out.println("test1");
}
}
class Child extends User{
public void test2(){
System.out.println("test2");
}
}
java中的类主要分为3种。
1.java核心类库中的类:string, Object
2.JVM软件平台厂商
3.我们自己写的类,比如:User,Child这些
类加载器也有3种
JDK 9+
- BootClassLoader 启动类加载器,如string, Object
- PlatformClassLoader 平台类加载器,如软件平台厂商。
在jdk8及以前的话,这个平台类加载器变成了扩展类加载器Extension ClassLoader - AppClassLoader 应用类加载器,如我们自己写的类
类加载器在加载时也是有顺序的:
java核心类库中的类(string, Object)==> 平台类(软件平台厂商) ==> 应用类加载器(自己写的类)
JDK 8 及之前是下面这3种
1.启动类加载器(Bootstrap ClassLoader)
2.扩展类加载器(Extension ClassLoader),在JDK 9+中,[扩展类加载器]变成了[平台类加载器]
3.应用类加载器(Application ClassLoader,也叫系统类加载器)
得到应用类加载器
package part;
public class Java01 {
public static void main(String[] args) {
// 获取类的信息,以前我们是通过new的形式,现在我们取到了Student这个类的信息。
Class<Student> studentClass = Student.class;
// 获取Student这个类的加载器
ClassLoader classLoader = studentClass.getClassLoader();
// 输出: sun.misc.Launcher$AppClassLoader@14dad5dc
// 得到的是:应用类加载器。为啥是应用类加载器呢?因为是我们自己写的类。所以是应用类加载器
System.out.println(classLoader);
}
}
class Student {
}
String类的类加载器为啥是null?
package part;
public class Java01 {
public static void main(String[] args) {
Class<String> stringClass = String.class;
ClassLoader classLoader = stringClass.getClassLoader();
// 输出null
System.out.println(classLoader);
}
}
class Student {
}
有些小伙伴看到这里会觉得很奇怪,为啥是null?
你不是说:string, Object 这些是启动器类吗?
按理说应该是:BootClassLoader(启动类加载器)才对。
因为:启动类加载器通常表示为null,因为它不是Java实现的。
解释一下启动类
启动类加载器是最顶层的,由原生代码实现,不继承java.lang.ClassLoader。
启动类加载器通常表示为null,因为它不是Java实现的。
得到平台类
我们可以理解为:平台类是应用类加载器的上一级的加载器。也就是说有有一个上一级的概念。
如果我们想要获取平台类的话。 通过自己写的类可以得到平台类。下面我们来尝试一下
package part;
public class Java01 {
public static void main(String[] args) {
// 获取类的信息,以前我们是通过new的形式,现在我们取到了Student这个类的信息。
Class<Student> studentClass = Student.class;
// 获取Student这个类的加载器
ClassLoader classLoader = studentClass.getClassLoader();
// 输出的是:在标准的JDK环境下(如JDK 8),输出会是:sun.misc.Launcher$ExtClassLoader@28d93b30
// JDK 9+ 由于模块化系统的引入,类加载器名称可能变为 jdk.internal.loader.ClassLoaders$PlatformClassLoader,但逻辑一致。
System.out.println(classLoader.getParent());
}
}
class Student {
}
在啰嗦说一下Java的类加载器层次结构
我们回忆Java的类加载器层次结构。通常,Java的类加载器分为三个层次:
启动类加载器(Bootstrap ClassLoader)、
扩展类加载器(Extension ClassLoader)、(这里根据版本不同,有所变化)
应用类加载器(Application ClassLoader,也叫系统类加载器)。
因此,启动类加载器通常表示为null,因为它不是Java实现的。
扩展类加载器负责加载JRE的扩展目录(如jre/lib/ext)中的类,而应用类加载器负责加载classpath下的类。
特别提醒:应用类加载器的父加载器是扩展类加载器,而扩展类加载器的父加载器是启动类加载器。
其中,启动类加载器是最顶层的,由原生代码实现,不继承java.lang.ClassLoader。
使用反射来登录
现在我们使用反射来练习一个登录功能。
如果用户名admin和密码123就算登录成功。然后返回true,否则返回false。
package part;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Java01 {
public static void main(String[] args) {
// 获取 Employee的类对象,这个类对象我们用employeeClass存储起来
Class<Employee> employeeClass = Employee.class;
try {
/**
* Constructor 类是用来表示类的构造方法的。
* getDeclaredConstructor 方法用于获取指定参数类型的构造方法,这里参数为空,所以是获取无参构造方法。
* newInstance()方法调用这个无参构造来创建Employee的实例。等价于直接调用 new Employee()
* */
Constructor<Employee> declaredConstructor = employeeClass.getDeclaredConstructor();
Employee employee = declaredConstructor.newInstance();
// 获取对象的属性,因为这个属性是公共的,所以使用getField
Field account = employeeClass.getField("account");
// 获取对象的属性,因为这个属性是公共的,所以使用getField
Field password = employeeClass.getField("password");
// 给属性赋值, 把employee这个对象中 account 属性赋值为:admin
account.set(employee, "admin");
// 给属性赋值, 把employee这个对象中 password 属性赋值为:123456
password.set(employee, "123456");
// 获取登录的方法 login
Method login = employeeClass.getMethod("Login");
// 调用哪个对象的方法?我调用的是:employee这个对象,我们调用这个方法,会返回一个结果,我们来给它接收一下
Object result = login.invoke(employee);
System.out.println(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class Employee {
public String account;
public String password;
// 如果用户名admin和密码123就算登录成功
public Boolean Login(){
// equals可以用来比较字符串是否相等的
return "admin".equals(account) && "123".equals(password);
}
}
尾声
准备开始学习java了。
今天学习的第10天,每天都会发文章,我要卷起来。
请小伙伴们监督我,奥利给
出处:https://www.cnblogs.com/ishoulgodo/
想问问题,打赏了卑微的博主,求求你备注一下的扣扣或者微信;这样我好联系你;(っ•̀ω•́)っ✎⁾⁾!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,或者关注博主,在此感谢!
万水千山总是情,打赏5毛买辣条行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主(っ•̀ω•́)っ✎⁾⁾!
想问问题,打赏了卑微的博主,求求你备注一下的扣扣或者微信;这样我好联系你;(っ•̀ω•́)っ✎⁾⁾!
![](http://images.cnblogs.com/cnblogs_com/IwishIcould/1900124/t_201214043958支付宝收款码.jpg?a=1607924145179)
![](http://images.cnblogs.com/cnblogs_com/IwishIcould/1900124/t_20121604194271E6E296CCB71A007F4E22073D5EB64A.jpg)
如果文中有什么错误,欢迎指出。以免更多的人被误导。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 本地部署 DeepSeek:小白也能轻松搞定!
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 如何基于DeepSeek开展AI项目