java(Class 常用方法 获取Class对象六种方式 动态和静态加载 类加载流程)

简介: java(Class 常用方法 获取Class对象六种方式 动态和静态加载 类加载流程)



Class常用方法

第一步:创建一个实体类

public class Car {
    public String brand = "宝马";
    public int price = 500000;
    public String color = "白色";
    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

第二步:常用方法的演示

public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        String classAllPath = "Reflection.Car";
        //获取到Car类 对应的 Class对象
        //<?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //输出cls
        System.out.println(cls); //显示cls对象,是哪个类的Class对象 class Reflection.Car
        System.out.println(cls.getClass());//输出cls运行类型 class java.lang.Class
        //得到包名
        System.out.println(cls.getPackage().getName());//包名
        //得到全类名
        System.out.println(cls.getName());
        //通过cls创建对象实例
        Car car = (Car)cls.newInstance();
        System.out.println(car);
        //通过反射获取属性 brand
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));//宝马
        //通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println(brand.get(car));//奔驰
        //得到所有的属性(字段)
        Field[] fields = cls.getFields();
        for(Field f:fields){
            System.out.println(f.getName());//名称
        }
    }
}

运行结果:




获取Class对象六种方式

第一种:在已知一个类的全类名,且该类在类的路径下,可通过Class类的静态方法Class.forName()获取(多用于配置文件,读取全类路径,加载类)

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.Class.forName
        String classAllPath = "Reflection.Car";//通过读取配置文件获取
        Class<?> cls1 = Class.forName(classAllPath);
    }
}

第二种:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //2.类名.class,应用场景:用于参数传递
        Class cls2 = Car.class;
    }
}

第三种:若已知某个类的实例,调用该实例的getClass()方法获取Class对象

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //3.对象.getClass(),应用场景:有对象实例
        Car car = new Car();
        Class cls3 = car.getClass();
    }
}

第四种:通过类加载器来获取到类的Class对象

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //4.通过类加载器[4种]来获取到类的Class对象
        String classAllPath = "Reflection.Car";
        //(1)先得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通过类加载器得到Class对象
        Class<?> cls4 = classLoader.loadClass(classAllPath);
    }
}

基本数据类型按如下方式得到Class类对象

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
    }
}

基本数据类型对应的包装类,可以通过.TYPE得到Class类对象

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Integer> type1 = Integer.TYPE;
        Class<Character> type2 = Character.TYPE;
    }
}

哪些类型有Class对象

1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2.interface:接口

3.数组

4.enum:枚举

5.annotation:注解

6.基本数据类型

7.void


动态和静态加载

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

假设我现在在代码里面放了一个没有创建的Dog类

public class ClassLoad_ {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入key");
        String key = sc.next();
        switch (key){
            case "1":
                Dog dog = new Dog();
                dog.cry();
                break;
            case "2":
                System.out.println("ok");
                break;
            default:
                System.out.println("do nothing");
        }
    }
}

导致在编译的时候就发生了报错,虽然我们在运行的时候不一定会使用到,因为new Dog()是静态加载,因此必须编写Dog


2.动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性

public class ClassLoad_ {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入key");
        String key = sc.next();
        switch (key){
            case "1":
                break;
            case "2":
                Class cls = Class.forName("Person");//加载Person类
                Object o = cls.newInstance();
                Method m = cls.getMethod("hi");
                m.invoke(o);
                System.out.println("ok");
                break;
            default:
                System.out.println("do nothing");
        }
    }
}

因为反射是动态加载,可以通过编译,只有在运行的时候才会报错,Person类是动态加载,所以,没有编写Person类也不会报错,只有动态加载该类的时候才会报错


1.当创建对象时(new) //静态加载

2.当子类被加载时,父类也加载 //静态加载

3.调用类中的静态成员时//静态加载

4.通过反射//动态加载


类加载流程




加载阶段

JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.long.Class对象

连接阶段

连接阶段-验证

目的是为了确定文件中字节流包含的信息符合当前虚拟机的要求,并且不会危害安全,可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间



连接阶段-准备

JVM会在该阶段对静态变量,分配内存默认初始化(对应数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配(常量和静态变量不一样,因为一旦赋值就不变,它直接就是它对应的值)

连接阶段-解析

虚拟机将常量池的符号引用替换为直接引用的过程中,在编译的过程中因为没有实际的内存地址,所以只能用符号的方法来记录,当加载好之后用地址来替换

初始化阶段

到初始化阶段,才真正开始执行类中定义的java程序代码,此阶段是执行()方法过程()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并
示例代码:

public class ClassLoad03 {
    public static void main(String[] args) {
        //1.加载B类,并生成B的class对象
        //2.连接 num = 0;
        //3.初始化阶段
        //  依次自动收集类中的所有静态变量的赋值动作和静态代码快的语句
        /*
* clinit(){
*   System.out.println("B 静态代码快被执行");
*    num = 300;
*
* 合并:num=100}*/
        new B(); 
        System.out.println(B.num);
    }
}
class B{
    static {
        System.out.println("B 静态代码快被执行");
        num = 300;
    }
    static int num = 100;
    public B(){
        System.out.println("B() 构造器被执行 "+num);
    }
}

运行结果:



如果直接使用类的静态属性,也会导致类的加载
注:
虚拟机保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到线程执行()方法完毕


获取类结构信息



public class ReflectionUtils {
    public static void main(String[] args) throws ClassNotFoundException {
        api_01();
    }
    //第一组方法API
    public static void api_01() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("Reflection.com.hspedu.classload.Person");
        //获取全类名
        System.out.println(personCls.getName());
        //获取简单类名
        System.out.println(personCls.getSimpleName());
        //获取所有public修饰的属性,包含本类及父类
        Field[] fields = personCls.getFields();
        for (Field field : fields) {
            System.out.println("本类及父类属性="+field.getName());
        }
        //获取本类中所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有的属性="+declaredField.getName());
        }
        //获取所有public修饰的方法,包含本类以及父类
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类以及父类的方法="+method.getName());
        }
        //获取本类所有的方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类所有方法="+declaredMethod.getName());
        }
        //获取所有public修饰的构造器,包含本类
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类以及父类的构造器="+ constructor.getName());
        }
        //获取本类所有的构造器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本类所有的构造器"+declaredConstructor.getName());
        }
        //以Package形式返回包信息
        System.out.println(personCls.getPackage());
        //以Class形式返回父类信息
        System.out.println("父类的class对象"+personCls.getSuperclass());
        //以Class[]形式返回接口信息
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("接口信息="+anInterface.getName());
        }
        //返回注解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息="+annotation);
        }
    }
}
interface IA{
}
interface IB{
}
class A{
    public String hobby;
    public void hi(){}
    public A(){}
}
@Deprecated
    class Person extends A implements IA,IB{
        public Person(){}
        public Person(String s){ }
        private Person(String name,int age){}
        //属性
        public String name;
        protected int age;
        String job;
        private double sal;
        //方法
        public void m1(){
        }
        protected void m2(){
        }
        void m3(){
        }
        private void m4(){
        }
    }



public class ReflectionUtils {
    public static void main(String[] args) throws ClassNotFoundException {
        api_02();
    }
    public static void api_02() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("Reflection.com.hspedu.classload.Person");
        //获取本类所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中的所有属性="+declaredField.getName()
                               +" 该属性的修饰符值="+declaredField.getModifiers()
                               +" 该属性的类型="+declaredField.getType());
        }
    }
}
interface IA{
}
interface IB{
}
class A{
    public String hobby;
    public void hi(){}
    public A(){}
}
@Deprecated
    class Person extends A implements IA,IB{
        public Person(){}
        public Person(String s){ }
        private Person(String name,int age){}
        //属性
        public String name;
        protected static int age;
        String job;
        private double sal;
        //方法
        public void m1(){
        }
        protected void m2(){
        }
        void m3(){
        }
        private void m4(){
        }
    }

目录
相关文章
|
18天前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
9天前
|
Java Maven Spring
Java Web 应用中,资源文件的位置和加载方式
在Java Web应用中,资源文件如配置文件、静态文件等通常放置在特定目录下,如WEB-INF或classes。通过类加载器或Servlet上下文路径可实现资源的加载与访问。正确管理资源位置与加载方式对应用的稳定性和可维护性至关重要。
|
11天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
21天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
42 17
|
15天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
32 2
|
17天前
|
Java 编译器 Maven
Java“class file contains wrong class”解决
当Java程序运行时出现“class file contains wrong class”错误,通常是因为类文件与预期的类名不匹配。解决方法包括:1. 确保类名和文件名一致;2. 清理并重新编译项目;3. 检查包声明是否正确。
|
21天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
17天前
|
Java Spring
JAVA获取重定向地址URL的两种方法
【10月更文挑战第17天】本文介绍了两种在Java中获取HTTP响应头中的Location字段的方法:一种是使用HttpURLConnection,另一种是使用Spring的RestTemplate。通过设置连接超时和禁用自动重定向,确保请求按预期执行。此外,还提供了一个自定义的`NoRedirectSimpleClientHttpRequestFactory`类,用于禁用RestTemplate的自动重定向功能。
|
21天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
19 0
|
2月前
|
Java
java基础(4)public class 和class的区别及注意事项
本文讲解了Java中`public class`与`class`的区别和注意事项。一个Java源文件中只能有一个`public class`,并且`public class`的类名必须与文件名相同。此外,可以有多个非`public`类。每个类都可以包含一个`main`方法,作为程序的入口点。文章还强调了编译Java文件生成`.class`文件的过程,以及如何使用`java`命令运行编译后的类。
38 3
java基础(4)public class 和class的区别及注意事项