Java-Day-34( Class 类特性 + Class 类常用方法 + Class 对象的获取方法 + 具有 Class 对象的类型 )

发布时间 2023-07-20 16:03:16作者: 朱呀朱~

Java-Day-34

Class 类特性

image-20230717112233407

  • Class 也是类,因此也继承 Object 类

  • Class 类对象不是 new 出来的,而是系统创建的

    • 通过类加载器 ClassLoader 类中的的 loadClass 方法
  • 对于某个类的 Class 类对象,在内存中只有一份

    • 不管是传统方法还是反射方法,一个类只会进一次 ClassLoad 类的 loadClass() 方法

      /*
       同是通过ClassLoad类加载 Dog 类的Class对象
       public Class<?> loadClass(String name) throws ClassNotFoundException {
       	 return loadClass(name, false);
       }
      */
      
      Class cls1 = Class.forName("com.hspJava.Dog");
      Class cls2 = Class.forName("com.hspJava.Dog");
      System.out.println(cls1.hashCode()); 
      System.out.println(cls2.hashCode());
      // hashCode值相同
      
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成

  • 通过 Class 可以完整地得到一个类的完整结构,通过一系列 API

  • Class 对象是存放在堆的

    • 在 Class 类阶段 ( 加载阶段 )
  • 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据 ( 包括方法代码、变量名、方法名、访问权限等等 )

    • 在加载阶段还会生成一个方法区,内含一个该类的字节码二进制数据 ——> 引入到堆内 Class 类对象的这层关系,二进制数据转成 Class 类对象 ( 其实是种数据结构 ) 后操作更加容易

Class 类的常用方法

String className = "com.zyz.Dog";
Class cls = Class.forName(className);
System.out.println(cls); // 输出为类所在路径,即指明是哪个类的Class对象:class com.zyz.Dog
System.out.println(cls.getClass()); // 输出为运行类型:class java.lang.Class
//        得到包名
System.out.println(cls.getPackage().getName()); // com.zyz
//        得到全类名
System.out.println(cls.getName()); // com.zyz.Dog
//        通过cls创建对象实例
Dog dog = (Dog) cls.newInstance(); //
System.out.println(dog); // com.zyz.Dog@5ca881b5
//        通过反射获取属性
Field name = cls.getField("name");
System.out.println(name.get(dog)); // 土豆
//        通过反射给属性赋值
name.set(dog, "球球"); // 属性.set在(对象, 赋新值)
System.out.println(name.get(dog)); // 球球
//        得到所有的属性(字段)
Field[] fields = cls.getFields();
for (Field f: fields) {
    System.out.print(f.getName() + "~"); // name~age~
}

Class 对象的获取方法

Class.forName()

  • 在代码 / 编译阶段
  • 前提:已知一个类的全类名,且该类在路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException
  • 应用场景:多用于配置文件,读取类全路径,加载类

类.class

  • Class 类 / 加载阶段 ( 堆中已有类对象 )
  • 前提:已知具体的类,该方式最为安全可靠,程序性能
  • 应用场景:多用于参数传递,比如通过反射得到对应构造器对象

对象.getClass

  • 就是之前一直用的获取运行类型的方法,就是在此运行阶段去找关联的在堆里的 Class 类对象,就是其在编译阶段真正加载进来的字节码文件的类,就是运行类型

  • 运行阶段 ( 对象已经有了 )

  • 前提:已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象

  • 应用场景:多用于有对象实例,即通过创建好的对象,获取 Class 对象

类加载器

  • 还可以通过类加载器得到 Class 对象

代码实例

//        1.Class.forName
String classAllPath = "com.zyz.Dog"; // 此处全路径应是通过读取配置文件获取
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);

//        2.类名.class
Class cls2 = Dog.class;
System.out.println(cls2);

//        3.对象.getClass()
Dog dog = new Dog();
Class cls3 = dog.getClass();
System.out.println(cls3);

//        4.通过类加载器[四种]来获取到类的Class对象
//        (1)先得到类加载器dog
ClassLoader classLoader = dog.getClass().getClassLoader();
//        (2)通过来加载器得到Class对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);

// 		  上述输出全为 class com.zyz.Dog

//        cls1、2、3、4其实是同一个对象,输出全部相同
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());

剩下两种比较特别的

  • 基本数据类型 ( int, char, boolean, float, double, byte, long, short ) 按如下方式得到 Class 类对象
Class cls = 基本数据类型.class;
  • 基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 类对象
Class type = 包装类.TYPE;
  • 代码实例 ( 可知 int 与 Integer 实际上哈希值都是一样的,只是底层在自动装箱和拆箱 )
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
//        看似为尖括号里的包装类,但输出可见还是最初的基本数据类型
System.out.println(integerClass); // int
System.out.println(characterClass); // char

Class<Integer> type = Integer.TYPE;
Class<Character> type1 = Character.TYPE;
System.out.println(type); // int
System.out.println(type1); // char

//        但int与Integer哈希值都是一样的,只是底层是自动装箱和拆箱
System.out.println(integerClass.hashCode());
System.out.println(type.hashCode());

具有 Class 对象的类型

  • 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  • interface:接口 —— 可看作一种特殊的类
  • 数组
  • enum:枚举
  • annotation:注解
  • 基本数据类型
  • void
Class<String> cls1 = String.class; // 外部类
Class<Serializable> cls2 = Serializable.class; // 接口
Class<Integer[]> cls3 = Integer[].class; // 数组
Class<float[][]> cls4 = float[][].class; // 二维数组
Class<Deprecated> cls5 = Deprecated.class; // 注解
Class<Thread.State> cls6 = Thread.State.class; // 枚举(线程中的State就是枚举enum修饰的)
Class<Long> cls7 = long.class; // 基本数据类型
Class<Boolean> cls8 = Boolean.class; // 包装类
Class<Void> cls9 = void.class; // void
Class<Class> cls10 = Class.class; // Class本身也是有的

System.out.println(cls1); //class java.lang.String
System.out.println(cls2); //interface java.io.Serializable
System.out.println(cls3); //class [Ljava.lang.Integer;
System.out.println(cls4); //class [[F
System.out.println(cls5); //interface java.lang.Deprecated
System.out.println(cls6); //class java.lang.Thread$State
System.out.println(cls7); //long
System.out.println(cls8); //class java.lang.Boolean
System.out.println(cls9); //void
System.out.println(cls10); //class java.lang.Class