Class类的理解

Class是在反射当中必须要用的,习惯上把Class称作反射的源头
关于Class类的理解:
1.类的加载过程:
Java程序编写完以后需要经过两个过程:程序经过javac.exe命令(是编译命令,执行编译过程)以后,会生成一个或多个字节码文件(以.class结尾),每一个类对应一个字节码文件。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于把这个字节码文件(所对应的类)加载到内存当中了,加载到内存中的过程就叫做类的加载。编译过程不在类的加载过程中。加载到内存中的类,就称作运行时类(因为是运行的时候进来的,所以这么叫),运行时类就作为Class的一个实例

Person是类型,Person.class是类本身。所以说类也是对象,是Class的一个对象
换句话说:Class类的实例对应着一个运行时类,所以想提供Class的一个实例,不能new Class,Class的一个实例就是用一个运行时类来进行赋值的
加载到内存中的运行时类,会缓存一定的时间,在此时间之内是唯一存在的,我们可以通过不同的方式来获取此运行时类
在运行时类的声明周期内是唯一的,不会去重复的加载第二个,或者第三个
获取Class实例的4种方式

public class test01 {


    //前三种方式掌握,最后一种了解,用的不多,方式三用的比较多,更能够体现动态性,编译时先不去确定
    @Test
    public void test1() throws ClassNotFoundException {
        //方式一:调用运行时类的属性.class
        Class clazz = Person.class;//Class带泛型,因为是Person给它赋的值,所以具体操作的是Person
        System.out.println(clazz);//class test.Person,不加泛型不会影响输出的效果,加上泛型可以避免后面的操作进行强转

        //方式二:通过运行时类的对象,调用getClass方法
        Person p1=new Person();
        Class clazz2 = p1.getClass();//得到这个对象是哪个类造的,此方法在Object类中进行声明,得到它所属的类本身,再赋给Class
        System.out.println(clazz2);//class test.Person

        //方式三:调用Class的静态方法:forName(String classPath)
        //不要写成Class.forName("Person");因为同一个module不同的包下可能都有Person,要说清楚是哪个包下的Person

        Class clazz3 = Class.forName("test.Person");//包含包名在内的类的全名,test是包名
        System.out.println(clazz3);//class test.Person
       // clazz3=Class.forName("java.lang.String");//自己定义的类和API定义的类都可以作为Class的实例
       // System.out.println(clazz3);//class java.lang.String

        System.out.println(clazz==clazz2);//true
        System.out.println(clazz==clazz3);//true

        //虽然方式不同,但获取的是内存中的同一个运行时类

        //方式四:使用类的加载器:ClassLoader
        //因为这个测试方法是写在test01这个类中的
        ClassLoader classLoader = test01.class.getClassLoader();

        //得到之后,显式地load class,即显式的去加载某一个类
        Class clazz4 = classLoader.loadClass("test.Person");
        System.out.println(clazz4);//class test.Person
        System.out.println(clazz==clazz4);//true

    }
//注意:如果Person类是写在当前module的src下面,直接"Person"即可,不要写成"src.Person"或者"src\\Person"

}

有没有其他的结构可以作为Class的一个实例?
比如说接口,接口也要加载到内存中
Class相当于对应着一个加载到内存中的结构,这个结构不仅仅是类
哪些类型可以有Class对象?
(1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类,即是类都可以,比如: Object.class
(2)interface:接口 ,比如 Comparable.class
(3)数组 ,比如 int[[].class
(4)enum:枚举类 ,比如ElementType.class。其中ElementType是枚举类
(5)annotation:注解@interface 比如Override.class
(6)primitive type:基本数据类型 比如int.class
(7)void(void也可以看成是类型,是没有返回值的意思)比如void.class
注意:Class c=Class.class;也可以,因为Class本身也是一个类
只要它们加载到内存中以后都可以看做是Class的一个实例

public class test01 {
    
    @Test
    public void test1() throws ClassNotFoundException {
        int[] a = new int[10];
        int[] b = new int[100];
        Class clazz1 = a.getClass();
        Class clazz2 = b.getClass();
        // 只要数组元素类型与维度一样,就是同一个
        System.out.println(clazz1==clazz2);//true,因为都是一维数组,元素类型都是int
    }
}

类的加载过程:
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
类的加载过程分成三个过程
1.类的加载(Load):把.class文件通过java.exe指令加载到内存中,并为之创建一个java.lang.Class对象,此过程由类加载器完成
2.类的链接(Link):将类的二进制数据合并到JRE中;设置类变量默认初始值
3.类的初始化(Initialize):JVM负责对类进行初始化
如果对static的属性既有显式赋值,又在静态代码块中进行了赋值,就看谁在前面,谁在前面谁就先赋值
显式赋值和静态代码块中赋值是在初始化阶段做的

class A { 
	static {
		 m = 300; 
 	} 
 	static int m = 100; 
} 

第一步是类的加载:把A加载到内存当中,就有一个Class实例,这个实例对应着方法区中的类
第二步是链接:静态变量m先赋值为0
第三步:初始化,由构造器方法完成< clinit>(),因为是先写静态代码块,后写显式赋值,所以会先把m赋值为300,再赋值为100,如果在测试类的main方法中输出A.m,得到的结果是100,如果把他们的顺序颠倒一下,得到的结果就是300

是类的加载器(也叫做类装载器)把类加载到内存当中的,之后在操作系统当中进行执行,分配内存空间,做运算……
运行时类是存放在方法区的
注意JVM垃圾回收机制可以回收这些Class对象,在被回收之前只存在一份

ClassLoader

public class test01 {

    @Test
    public void test(){

        //获取自定义类的类的加载器,对于自定义类是使用系统类加载器进行加载
        ClassLoader classLoader = test01.class.getClassLoader();//test01.class是去获取Class类的一个实例
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //调用系统类加载器的getParent方法可以获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();//即得到上一层,扩展类加载器
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@452b3a41

        //通过扩展类加载器调用getParent没有办法获取引导类加载器
        //引导类加载器主要负责加载java核心类库,无法加载自定义类
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null,是存在的,只是我们无法直接获取到引导类加载器


        //有了类的加载器之后可以用loadClass主动加载某个类,因为获取不到引导类构造器,所以也无法让它去加载自定义类


        ClassLoader classLoader3 = String.class.getClassLoader();//是引导类加载器
        System.out.println(classLoader3);//null
        //String是引导类加载器加载的,并且无法获取引导类加载器
    }

}

集合当中有Properties:用来读取配置文件
键值都是String
读取配置文件的方式一;

public class test01 {

    @Test
    public void test() throws Exception {

        Properties pro=new Properties();

        //因为是单元测试方法,所以是相当于当前的module下,如果没有module就是相对于当前工程而言,就可以写成"jdbc.properties"

        FileInputStream fis=new FileInputStream("jdbc.properties");//指定具体的文件地址,或者说配置文件,可以new File,也可以new Resource Bundle,如果是采用new File的方式还要加上.properties的后缀



        //如果配置文件放在module的src下,要写成"src\\jdbc.properties"
        //如果配置文件放在module的src的test包下,要写成"src\\test\\jdbc.properties"
        //把配置文件中的数据读进来,呈现在控制台上


        pro.load(fis);//要传输入流,加载对应的流,操作对应的配置文件

        String user = pro.getProperty("user");
        String password = pro.getProperty("password");
        System.out.println("user="+user+",password="+password);//user=吴飞,password=abc123
    }

}

读取配置文件的方式二:使用ClassLoader
如果是采用相对路径的方式,则配置文件默认识别为当前module的src下,如果没module就是当前project的src下。这里要注意和方式一的区别

  @Test
    public void test1() throws IOException {
        //拿到类的加载器,通过类的加载器去做

        Properties pro=new Properties();
        ClassLoader classLoader = test01.class.getClassLoader();
       // InputStream is = classLoader.getResourceAsStream("jdbc.properties");//以流的方式获取资源
       // pro.load(is);//报空指针异常


        //注意此时的识别位置不在module(没有module就是project)下,而是在src下,不是src下面的包下,而是在src下

        //我们右键src建立配置文件
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pro.load(is);

        String user = pro.getProperty("user");
        String password = pro.getProperty("password");
        System.out.println("user="+user+",password="+password);//user=吴飞,password=abc123
    }

如果第一种方式想要去加载src下的配置文件
就写成这样

FileInputStream fis=new FileInputStream("src\\jdbc.properties");

因为还是针对当前module下,有一个src目录,下面有一个配置文件为jdbc.properties

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值