类加载&反射&模块化

类加载

类加载原理

当程序要使用某一个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤对类进行初始化,如果不出现意外的情况,JVM虚拟机会连续的完成这三个步骤,所以有时也把这三个步骤统称为类加载,或类初始化

类的加载:

  • 就是将class文件读入内存,并为之创建一个java.lang.Class对象

  • 任何类被使用时,系统都会为之建立一个java.lang.Class对象

类的链接:

  • 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
  • 准备阶段:负责为类的类变量(static)分配内存,并设置默认初始值
  • 解析阶段:将类的二进制数据中的符号引用替换为直接引用(虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程)

类的初始化:

主要是对类变量进行初始化(类构造器是构造类信息的,不是构造该类对象的构造器)

类的初始化步骤:

  • 假如类还未被加载和链接,则程序先加载再链接该类

  • 假如类的直接父类还未被初始化,则先初始化该类的直接父类

  • 假如类中有初始化语句,则系统依次执行这些初始化语句(保证一个类的()方法在多线程环境中被正确加锁和同步)

  • 类的初始化时机(类的主动引用):

    • 创建类的实例
    • 调用类的类方法
    • 访问类或接口的类变量,或者为该类变量赋值
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某类的子类
    • 直接使用java.exe 命令来运行某个主类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l5Qa5Lp5-1657251461048)(…/…/…/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20220707234706580.png)]

类的被动引用(不会发生类的初始化):

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化(父)。如当通过子类引用父类的静态变量,不会导致子类初始化

  • 通过数组定义类的引用,不会触发此类的初始化

  • 引用常量不会触发此类的初始化,static final 常量在链接阶段就存入类的常量池中

类加载器

ClassLoader:是负责加载类的对象

Java运行时具有以下内置类加载器

  • 启动类加载器(Bootstrap ClassLoader):它是虚拟机的内置类加载器,通常表示为NULL,负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
  • 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
  • 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。(System Class Loader)
package yu;

public class Demo1 {
    public static void main(String[] args) {
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();//返回用于委派的系统类加载器
        System.out.println(systemClassLoader);
        ClassLoader parent = systemClassLoader.getParent();//返回父类加载器进行委派
        System.out.println(parent);
        ClassLoader parentParent = parent.getParent();
        System.out.println(parentParent);// BootstrapClassLoader
        // 是虚拟机的内置类加载器通常为null
    }
}

反射

反射机制:

Java反射机制:是指在运行期去获取一个类的变量和方法信息。然后通过获取到的信息类创建对象,调用方法的一种机制。由于这种动态性,可以极大的提高程序的灵活性。程序不用在编译期就完成确定,在运行期仍可以扩展

  • Class 类是所有.class文件对象所对应的类型

  • Class 类中有成员变量,构造方法,成员方法…是对具体类的映射

1.反射获取Class对象

我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是Class类型的对象,下面有三种获取Class 类型的方式

  • 使用类的class属性来获取该类对应的Class对象,举例:Student.class将会返回Student类对应的Class对象
  • 调用对象的getClass()方法,返回该对象所属类的Class对象,该方法是Object类中的方法,所有的Java对象都可以调用该方法
  • 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径(完整包名的路径)
public class Demo2 {
    public static void main(String ...args) throws ClassNotFoundException {
        //方式1通过调用类的getClass()方法获取Class字节码文件对象
        Class<? extends Student> aClass = new Student("nb", 5, "马牛皮").getClass();
        System.out.println(aClass);
        //方式2通过类的class属性获取Class的字节码文件对象
        Class<Student> studentClass = Student.class;
        System.out.println(studentClass);
        //方式3 使用Class类中的静态方法forName获取Class的字节码文件对象
        Class<?> aClass1 = Class.forName("lff.xm.Student");
        System.out.println(aClass1);
    }
}

2.反射获取构造器方法并使用

Class 类中用于获取构造方法的方法

  • Constructor<?>[] getConstructors(): 返回所有public 构造方法对象的数组

  • Constructors<?>[] getDeclaredConstructors():返回所有构造方法对象的数组

  • Constructor getConstructor(Class<?>…parameterTypes):返回单个public 构造方法对象

  • ConstructorgetDeclaredConstructor(Class<?>… parameterTypes):返回单个构造方法对象

  • 基本数据类型也可以通过.class得到对应的Class类型
    
    • Constructor 类中用于创建对象的方法
    • T newInstance(Object…initargs):根据指定的构造器方法去创建对象
package lff.xm;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> c = Student.class;
        //拿到public 修饰的构造方法数组
        Constructor<?>[] constructors = c.getConstructors();
        for (Constructor con : constructors) {
            System.out.println(con);
        }
        //拿到类声明的所有 构造方法数组
        Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
        for (Constructor con : declaredConstructors) {
            System.out.println(con);
        }
        System.out.println("----------------");
        //通过字节码文件对象得到了单个的构造方法对象con;
        Constructor<Student> con = c.getConstructor();
        Constructor<Student> con2 = c.getConstructor(String.class, int.class, String.class);//基本数据类型也可以通过.class得到对应的Class类型
        // 通过构造方法对象调用newInstance 创建一个该类(Student)的对象
        Student student1 = con.newInstance();
        Student student2 = con2.newInstance("李肥肥",5,"山东");
        System.out.println(student1);
        System.out.println(student2);

    }
}

注意点:

  • public void setAccessable(true) 表示反射对象应该在使用Java语言访问控制时抑制检查。
//正常情况不能使用私有构造方法创建对象,在反射中可以暴力反射   setAccessible(true)
Class<Student> c3 = Student.class;
Constructor<Student> con3 = c3.getDeclaredConstructor(int.class);
con3.setAccessible(true);
Student student3 = con3.newInstance(18);
System.out.println(student3);

3.反射获取成员变量并使用

成员变量字段field

  • public void setAccessable(true) 表示反射对象应该在使用Java语言访问控制时抑制检查。
  • Field [] getFields():返回所有公共成员变量对象的数组
  • Field [] getDeclareFields():返回所有成员变量的对象的数组
  • Field [] getField(String name):返回单个公共成员变量对象
  • Field [] getDeclareField(String name) 返回单个成员变量对象
package yu;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 字段用法
 */
public class Demo4 {
    /**
     * @param args String类型的可变参数
     * @throws NoSuchFieldException 抛出异常
     */
    public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> studentClass = Student.class;
        System.out.println("public修饰的字段:");
        Field[] fields = studentClass.getFields();
        for (Field field : fields)
            System.out.println(field);
        System.out.println("类中所有声明的字段:");
        Field[] fields1 = studentClass.getDeclaredFields();
        for (Field field : fields1)
            System.out.println(field);
        System.out.println("获取单个public修饰的字段:");
        Field field = studentClass.getField("address");
        System.out.println(field);
        System.out.println("获取单个任意修饰的字段:");
        Field field1 = studentClass.getDeclaredField("name");
        System.out.println(field1);
        Class<?> aClass = Class.forName("yu.Student");
        Constructor<?> c = aClass.getDeclaredConstructor(String.class, int.class, String.class);
        Object o = c.newInstance("肖猛", 18, "东明");
        //正常情况不能使用私有属性赋值,在反射中可以暴力反射   setAccessible(true)
        //public void setAccessable(true) 表示反射对象应该在使用Java语言访问控制时抑制检查。
        field1.setAccessible(true);
        field1.set(o,"李振宇");
        Student s=(Student)o;
        System.out.println(s);
    }
}

4.反射获取成员方法并使用

Class类中用于获取成员方法的类

  • Method[] getMethods() : 返回所有公共成员方法对象的数组,包括继承的
  • Method[] getDeclareMethods() : 返回所有成员方法对象的数组,不包括继承的
  • Method[] getMethod() : 返回单个公共成员方法的对象
  • Method[] getDeclaremethods() : 返回单个成员方法对象

Mehod类中用于调用成员方法的方法

  • Object invoke (Object obj,Object…args): 调用obj 对象的成员方法,参数args 例如:(int.class),返回值是Object类型
package yu;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 反射获取成员方法
 */
public class Demo5 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> cs = Student.class;
        Method[] methods = cs.getMethods();
        for(Method method:methods)
            System.out.println(method);
        System.out.println("------------");
        Method[] declaredMethods = cs.getDeclaredMethods();
        for(Method method:declaredMethods)
            System.out.println(method);
        System.out.println("------------");

        Constructor<Student> constructor = cs.getDeclaredConstructor();
        Student student = constructor.newInstance();
        Method m1 = cs.getDeclaredMethod("method1",int.class);
        Object o = m1.invoke(student, 10);
        System.out.println((boolean)o);
        System.out.println("--------------");
        Method method2 = cs.getDeclaredMethod("method2");
        //暴力反射,忽略检查
        //public void setAccessable(true) 表示反射对象应该在使用Java语言访问控制时抑制检查。
        method2.setAccessible(true);
        method2.invoke(student);
    }
}

5.反射操作泛型

Java采用泛型擦除的机制类引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的雷响全部擦除(Class对象中有所保留)

为了通过反射操作这些类型,Java新增了ParameterizedType(参数化类型),GenericArrayType(泛型数组型),TypeVariable(类型变量),WildcardType (通配符类型) 这几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型

  • ParameterizedType(参数化类型) ,表示一种参数化类型,例如:List(Integer)
  • GenericArrayType(泛型数组型),表示一种元素类型是参数化类型或类型变量的数组类型
  • TypeVariable(类型变量),是各种类型变量的公共接口
  • WildcardType (通配符类型), 代表一种通配符类型表达式
package com.xm;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * 反射获取泛型
 */
public class Demo2 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Demo2> demo2Class = Demo2.class;
        //参数类型是泛型
        Method t1 = demo2Class.getMethod("test01", Map.class);
        Type[] genericParameterTypes = t1.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);//打印Map
            //泛型的参数类型属于参数化类型
            if (genericParameterType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);//打印Map中的类型
                }
            }
        }
        //返回值类型是泛型
        System.out.println("----------------------");
        Method t2 = demo2Class.getMethod("test02", null);
        Type returnType = t2.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

    public void test01(Map<Integer, Student> map) {
        System.out.println("test");
    }

    public List<String> test02() {
        System.out.println("test2");
        return null;
    }
}

6.反射越过泛型检查

向ArrayList 集合中添加字符串数据

package yu;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class TestDemo {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        ArrayList<Integer> array=new ArrayList<>();
        array.add(12);
        array.add(23);
        array.add(34);
        Class<? extends ArrayList> aClass = array.getClass();
        /*
        反射越过泛型检查
         */
        Method method = aClass.getMethod("add", Object.class);
        method.invoke(array,"你好");
        method.invoke(array,"世界");
        method.invoke(array,45);
        for (Object obj:
             array) {
            System.out.println(obj);
        }
    }
}

7.运行配置文件指定内容

package yu;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 通过配置文件运行类中的方法
 */
public class TestDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        /*
        加载数据
         */
        Properties prop = new Properties();
        FileReader fr = new FileReader("test.txt");
        prop.load(fr);
        fr.close();
        /*
        由键获取值
         */
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");
        /*
        通过反射建造对象,调用方法
         */
        Class<?> aClass = Class.forName(className);
        Constructor<?> constructor = aClass.getDeclaredConstructor();
        Object o = constructor.newInstance();
        Method declaredMethod = aClass.getDeclaredMethod(methodName);
        declaredMethod.invoke(o);


    }
}

class Student1 {
    public void study() {
        System.out.println("好好学习");
    }
}

class Teacher1 {
    public void teach() {
        System.out.println("教授知识");
    }
}

test.txt

className=yu.Teacher1
methodName=teach

模块化

模块化的概述

即使程序只需要使用Java的部分核心功能,JVM 也要加载整个JRE环境

为了给java “瘦身”,让Java实现轻量化,Java9正式的推出了模块化系统。Java被拆分为N个模块,并允许Java程序可以根据需要选择加载程序必须的Java模块,这样就可以让Java以轻量化的方式来运行

其实,Java7 已经提出了模块化的概念,但是由于过于复杂,Java7 Java8 都没有真正推出,直到Java9 才真正的成熟起来,对于Java语言来说,模块化系统是一次真正的自我革新,使得Java语言重新焕发活力

项目中包含多个模块,每个模块包含多个包,运行程序往往只需要部分模块功能,不用加载整个模块

模块的基本使用

  • 在模块的src目录下新建一个module-info.java的描述性文件,该文件专门定义模块名,访问权限,模块依赖等信息 ;描述性文件中使用模块导出和模块依赖来进行配置和使用;
  • 模块中所有未导出的包都是模块私有的,他们时不能在模块之外被访问的,在A模块下的描述性文件中配置模块导出 ;模块导出格式:exports 包名
  • 模块B要访问其他模块,则必须指定依赖哪些模块,未明确指定依赖关系的模块不能访问,在B模块下的描述性文件中配置模块依赖;模块依赖格式:requires 模块名;
  • 这里有点坑:写requires 模块名时,会报错,需要按下CTRL +1 或者 Alt+ Enter,根据改进信息,选择模块依赖
  • 然后模块B中就可以使用模块A中的内容了(导入类之类的操作不可少)

Student

package com.xm;

public class Student {
    public void study(){
        System.out.println("好好学习");
    }
}

module-info.java (A) 和 module-info.java(B)

module A {
    exports com.xm;
}
module B {
    requires A;
}

测试类

package meng.test;

import com.xm.Student;

/**
 * 模块的基本使用
 */
public class Demo1 {
    public static void main(String[] args) {
        new Student().study();
    }
}

模块服务的使用

服务:从Java 6开始,Java提供了一种服务机制,允许服务提供者和服务使用者之间完成解耦简单的说,就是服务使用者只面向接口编程,但不清楚服务提供者的实现类

Java 9 的模块化系统则进一步的简化了Java的服务机制。Java9 允许服务接口定义在一个模块中,并使用uses语句来声明该服务接口,然后针对该服务接口提供不同的服务实现类,这些服务实现类可以分布在不同的模块中,服务实现模块则使用provides语句为服务接口指定实现类 ,服务使用者只需要面向接口编程即可

模块使用步骤:

  • 在A模块中创建一个包 com.server,在该包下提供一个接口,接口中定义一个抽象方法public interface MyServer{ void server(); }
  • 在com.server包下创建一个包impl ,在该包中创建两个实现类Game1和Game2实现MyServer
  • 在A模块下的描述性文件中添加如下配置
    • 模块导出 exprots com.server;
    • 服务提供:provides MyServer with Game1; 指定MyServer的服务实现类是Game1
  • 在B模块下的描述文件中添加如下配置: 声明服务接口:use MyService;
  • 在B模块的类中使用MyServer接口提供的服务: ServiceLoader : 一种加载服务实现类的工具

MyServer接口

package com.server;

public interface MyServer {
    void service();
}

实现类game1和game2

package com.server.impl;

import com.server.MyServer;

public class Game1 implements MyServer {

    @Override
    public void service() {
        System.out.println("实现Game1");
    }
}



package com.server.impl;

import com.server.MyServer;

public class Game2 implements MyServer {
    @Override
    public void service() {
        System.out.println("实现game2");
    }
}

module-info.java(A)和module-info.java(B)

import com.server.MyServer;
import com.server.impl.Game1;
import com.server.impl.Game2;

module A {
    exports com.xm;
    exports com.server;
    provides MyServer with Game2;
    //provides MyServer with Game1;
}

import com.server.MyServer;

module B {
    requires A;
    uses MyServer;
}

测试类

package meng.test;

import com.server.MyServer;

import java.util.ServiceLoader;

public class Demo2 {
    public static void main(String[] args) {
        //加载服务
        ServiceLoader<MyServer> myServers = ServiceLoader.load(MyServer.class);
        //遍历服务
        for (MyServer ms :
                myServers) {
            ms.service();
        }
    }
}

扩充知识

动态语言:

是一类在运行时可以改变其结构的语言:例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他的结构上的变化。通俗点说就是运行时代码可以根据某些条件改变自身的结构

动态语言:Object-C ,C#,JavaScript , PHP, Python等

静态语言:

与动态语言相对应,运行时结构不可变的语言就是静态语言。 静态语言: Java,C,C++;

注意:Java 不是动态语言,但是Java被称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活

反射优点:

可以实现动态的创建对象和编译,非常灵活

反射缺点:

对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且,它可以满足我们的要求。这类操作总是慢于直接执行相同的操作(正常new)。

获取Class 对象:

基本内置类型的包装类都有一个Type 属性

Class c1=Integer.TYPE;

获取父类Class对象:

Class c2=c2.getSuperclass();

所有类型的Class对象

package com.xm;

import java.lang.annotation.ElementType;

public class Demo1 {
    public static void main(String[] args) {
        Class<Object> c1 = Object.class;//类
        Class<Comparable> c2 = Comparable.class;//接口
        Class<String[]> c3 = String[].class;//一维数组
        Class<int[][]> c4 = int[][].class;//二维数组
        Class<Override> c5 = Override.class;//注解
        Class<ElementType> c6 = ElementType.class;//枚举
        Class<Integer> c7 = Integer.class;//基本数据类型
        Class<Void> c8 = void.class;//void
        Class<Class> c9 = Class.class;//Class本身
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}

下篇文章 Java注解

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ILFFQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值