柏竹 柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档

柏竹

奋斗柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档
  • Java基础

    • Java认识
    • 面向对象概述
    • Java API
    • Java三大特性
    • Java类的高级特性
    • Java异常
    • Swing程序设计
    • Java集合类
    • Java I/O
    • Java反射
      • 类加载
        • 类的生命周期
      • 加载器
        • 启动类加载器
        • 扩展类加载器
        • 应用类加载器
        • 双亲委派
      • 反射
        • 类对象
        • 构造方法
        • 属性
        • 方法
        • 注解
        • Book类
        • 注解MyAnnotationField
        • 注解MyAnnotationTable
      • 加载配置文件
    • 枚举
    • 泛型
    • Java线程
    • Java网络通信
    • Java事件
    • AWT绘图
    • Java网页访问
    • XML&JSON
    • Java注解
  • JavaWeb

  • 拓展技术

  • 框架技术

  • 数据库

  • 数据结构

  • Spring

  • SpringMVC

  • SpringBoot

  • SpringClound

  • Ruoyi-Vue-Plus

  • 后端
  • Java基础
柏竹
2021-03-06
目录

Java反射

# Java反射

# 类加载

# 类的生命周期

类一共有7生命周期的阶段分别为:

  1. 加载

    • 通过类的 全限定名 找到 .class字节码文件,加载至JVM中
    • JVM 在内存中生成该类的Class对象,作为入口(JVM不管多少次使用该类,而 JVM 只会生成一个该对象的Class对象)
  2. 验证

    • 文件格式:检验是否符合 .class文件、头文件等
    • 元数据:(以下说明)
    • 字节码:检验 代码 语句/逻辑 是否符合
    • 字符引用:检验 类路径/修饰符的使用 是否符合
  3. 准备 如果该类有类对象(类被加载生成的Class对象),则开始为该类进行分配内存空间以及赋予初始化的值。例如:

    // 该阶段是 赋予的值是初始值0,而不是赋值18
    public static int age = 18;
    // 因为常量,赋予的值不是初始值,而是赋值18
    public static final int  age = 18;
    
  4. 解析 将常量池的 符号引用 替换为 直接引用

  5. 初始化

    • 执行类构造器
    • 如果有 静态变量,静态代码块,则在上一阶段被执行
    • 如果有 父类,则先执行其父类的构造器
  6. 使用 调用实例的 属性/方法

  7. 卸载 JVM垃圾回收

元数据:

  1. 是否有父类
  2. 是否继承了不允许被继承的类 (final修饰)
  3. 如果该类不是抽象类,是否实现其 父类/接口 中的实现方法
  4. 类中的 字段/方法 是否无误

# 加载器

负责把 .class字节码文件 加载到 JVM内存 中,并且生成对象

JVM中默认有三种类加载器:

  • 启动类加载器 BootstrapClassLoader
  • 扩展类加载器 ExtensionClassLoader
  • 应用类加载器 App ClassLoader

# 启动类加载器

启动类加载器 BootstrapClassLoader,Java类加载器是根据类全限定名(包含有包路径)来读取类的 二进制字节流 到 JVM 中,然后加载到JVM的内存中

  • 主要加载 jre/lib 核心库
  • 通过 C++ 进行创建(本地系统语言代码)
  • 开发者不能直接 调用该库进行操作

# 扩展类加载器

扩展类加载器 ExtensionClassLoader 是Java编写,且它的父类加载器是启动类加载器 由 sun.misc.Launcher$ExtClassLoader类 实现,主要加载 JAVA_HOME/lib/ext 目录中的类库(也可以更改系统里的环境变量,指定路径目录)

  • 主要加载 lib/ext 下的库文件
  • 通过 Java执行
  • 开发者 可以使用标准扩展类加载器

# 应用类加载器

应用类加载器 App ClassLoader,且它的父类加载器是 扩展类加载器 由 sun.misc.Launcher$AppClassLoader类 实现,主要加载应用程序 java -classpath/-Djava.class.path 目录下所有 jar和class文件

  • 主要加载 开发者 自己编写的类
  • 通过 Java执行

类加载器间的关系

  • 启动类加载器,由C++实现,没有父类
  • 扩展类加载器,由Java实现,父类加载器 启动类加载器
  • 应用类加载器,由Java实现,父类加载器 扩展类加载器

类加载器的唯一性

JVM中有两个类,判断两类是否相同,前提两类是同一个加载器加载的!否则它们不是同一个类

# 双亲委派

它们并非是通常的类继承关系,而是采用组合关系来复用父类加载器的方式加载!

工作原理

类加载器收到类加载请求,它并不会自己去加载,而是把请求委托给父类的加载器去执行加载,如果父类加载器还存在有父类加载器,则进一步向上委托,以此传递请求委托,直至 顶层的启动类加载器。如果顶层加载器加载失败,则将请求返回给 子加载器 尝试自己去加载,依旧向下传递委托。如果加载器完成类加载,则直接成功返回,无需传递请求!委派的好处就是避免有些类被重复加载

故事描述: (坑爹)

他爸生有两个儿子,儿子特别懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子只好自己想办法去完成。。。

顶层类加载器是ClassLoader类 , 是一个抽象类,loadClass()方法是ClassLoader类自己实现的,该方法中的逻辑就是双亲委派模式的实现,代码示例:

protected synchronized Class<?> loadClass(String name , boolean resolve) throws ClassNotFoundException{
    //检查是否被加载
    Class c = findLoadedClass(name);
    //如果没加载,则调用父类加载器
    if(c == null){
        try{
            //父类加载器不为空
            if(this.parent != null){
                // 请求委托给父类
                c = this.parent.c(name , false);
            }else{
                //父类加载器为空,则使用启动类加载器(执行到这里代表加载到顶层加载器)
                c = findBootstrapClassOrNull(name);
            }
        }catch(ClassNotFoundException e){
            //如果父类加载器加载失败,则抛回给子类运行
            c = findClass(name);
        }
    }
    if(resolve){
        resolveClass(c);
    }
    return c;
}

# 反射

Java反射机制,可以在程序中访问 已经写好类和方法 的Java对象的描述,实现访问、检测、修改Java本身的信息 等功能

  • 运行中获取
  • 解析类
  • 操作类

# 类对象

java.lang.Class<T> 类对象 是描述类的类

获取对象类的类的前提,必须该类在运行时已经加载至内存(JVM在加载器会自动加载)

获取类对象的方式

  1. 静态方法 ==Class.forName(全限定名)==
  2. 对象方法 ==实例对象.getClass()==
  3. 对象属性 ==实例对象.class==

注意:

  • 在调用时, 如果类在内存中不存在, 则会加载到内存! 如果类已经在内存中存在, 不会重复加载, 而是重复利用 !
  • Class类对象 不管获取多少个相同的对象,它们的地址始终都是一样
package com.sans;

// 类的获取
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
       
        Class class1 = Class.forName("com.sans.People");
        Class class2 = com.sans.People.class;
        People people = new People();
        Class class3 = people.getClass();
    
        System.out.println(class1);
        System.out.println(class2);
        System.out.println(class3);
        
        // 比较地址
        System.out.println(class1 == class2);
        System.out.println(class1 == class3);
        System.out.println(class2 == class3);
    }
}
class People{}

/** 控制台结果
 class com.sans.People
 class com.sans.People
 class com.sans.People
 true
 true
 true
 */

# 构造方法

java.lang.reflect.Constructor<T> 反射应用的 构造方法 对象

获取构造方法 的前提 需要获取指定类的对象。通过类对象的方法获取构造方法

获取构造方法:

返回 方法 说明
Constructor ==getConstructor(Class<?>···parameterTypes)==
示例:==getConstructor(int.class,String.class)==
获取 权限为public的指定构造方法
Constructor[] ==getConstructors()== 获取 所有权限为public的构造方法
Constructor ==getDeclaredConstructor(Class<?>···parameterTypes)== 获取 所有权限的单个构造方法
Constructor[] ==getDeclaredConstructors()== 获取 所有权限的全部构造方法,按声明顺序排列(所有权限)

Constructor类 常用方法: (获取更多方法自行API)

返回 方法 说明
String ==getName()== 获取 构造方法的名字
Class[] ==getParameterTypes()== 获取 构造方法参数类型的数组
boolean ==isVarArgs()== 是否带有可变数量的参数
Class[] ==getExceptionTypes()== 获取 构造方法可能抛出的异常类型
Object T ==newInstance(Object··· initargs)== 指定参数创建 该类对象的构造方法。如方法无参,则创建无参的构造方法
void ==setAccessible(boolean bool)== 是否无视访问权限检查

通过 java.lang.reflect.Modifier类 来解析部分无法识别 构造方法 的信息,比如==getModifiers()== 返回的值是需要解析的

package com.sans.constructor;

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

public class Test {
    public static void main(String[] args) throws Exception {
        
        Class<Student> sClass = (Class<Student>) Class.forName("com.sans.constructor.Student");
        
        //No.1 获取无参
        Constructor<Student> c1 = sClass.getConstructor();
        Student s = c1.newInstance();
        System.out.println(s);
        
        System.out.println("==============");
        
        //No.2 获取指定参数类型的
        Constructor<Student> c2 = sClass.getConstructor(String.class , int.class);
        Student s2 = c2.newInstance("张三" , 22);
        System.out.println(s2);
        
        System.out.println("==============");
        
        //No.3 获取所有权限及构造方法
        Constructor<Student>[] c3 = (Constructor<Student>[]) sClass.getDeclaredConstructors();
        Student s3;
        for (Constructor<Student> container : c3) {
            System.out.println(container);
        }
        
        
    }
}

class Student {
    String name;
    int age;
    
    public Student(){}
    
    private Student(String...strs){
        for (String s : strs) {
            System.out.println(s);
        }
    }
    
    public Student(String name , int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

/*	控制台结果

Student{name='null', age=0}
==============
Student{name='张三', age=22}
==============
public com.sans.constructor.Student(java.lang.String,int)
private com.sans.constructor.Student(java.lang.String[])
public com.sans.constructor.Student()

*/

# 属性

java.lang.reflect.Field 反射应用的 属性 对象 通过方法访问成员变量,将返回Field类型对象,每个Field对象代表一个成员变量 **注意: ** 属性必须要有修饰符修饰才可以获取类属性

获取属性

返回 方法 说明
Field ==getField(String name)== 获取 指定属性
Field ==getDeclaredField(String name)== 获取 所有权限的 指定属性
Field[] ==getFields()== 获取 所有属性
Field[] ==getDeclaredFields()== 获取 所有权限的全部属性,按声明顺序排列

Field类 常用方法 (获取更多方法自行API)

返回 方法 说明
String ==getName()== 获取 属性名
class ==getType()== 获取 该是属性类型的class对象
Object ==get(Object obj)== 指定对象obj中成员变量的值进行返回
void ==set(Object obj , Object value)== 指定对象obj中成员变量的值置为value
void ==setAccessible(boolean flag)== 设置是否忽略权限限制直接访问私有成员
int ==getModifiers()== 获取该成员变量修饰符的整型
package com.sans.field;

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

public class Test {
    public static void main(String[] args) throws Exception {
        
        // 反射实例对象
        Class<Person> aClass = (Class<Person>) Class.forName("com.sans.field.Person");
        Constructor<Person> ct = aClass.getConstructor(String.class, int.class, String.class);
        Object o = ct.newInstance("柏竹" , 20 , "18122335634");
        
        //No.1 直接获取
        Field name = aClass.getField("name");
        System.out.println(name);
        
        System.out.println("=============");
        
        //No.2 获取所有权限 获取手机号码 private获取
        Field phoneNumber = aClass.getDeclaredField("phoneNumber");
        // 无视权限访问
        phoneNumber.setAccessible(true);
        System.out.println(phoneNumber);
        
        System.out.println("=============");
        
        //No.3 获取 所有属性
        //无法访问私有属性
        for (Field tmp : aClass.getFields()) {
            System.out.println(tmp);
        }
        
        System.out.println("=============");
        
        /*
         * 方法使用
         * */
        System.out.println("------获取属性名");
        System.out.println(" ["+phoneNumber.getName()+"] ");
        
        System.out.println("------获取属性类型");
        System.out.println(" ["+phoneNumber.getType()+"] ");
        
        System.out.println("------获取属性值");
        System.out.println(" ["+phoneNumber.get(o)+"] ");
        
        System.out.println("------设置属性值");
        phoneNumber.set(o , "18122334455");
        System.out.println(" ["+o+"] ");
    }
}

class Person{
    public String name;
    public int age;
    private String phoneNumber;
    
    public Person() {
    }
    
    public Person(String name , int age , String phoneNumber) {
        this.name = name;
        this.age = age;
        this.phoneNumber = phoneNumber;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phoneNumber=" + phoneNumber +
                '}';
    }
}

/* 控制台结果

public java.lang.String com.sans.field.Person.name
=============
private java.lang.String com.sans.field.Person.phoneNumber
=============
public java.lang.String com.sans.field.Person.name
public int com.sans.field.Person.age
=============
------获取属性名
 [phoneNumber] 
------获取属性类型
 [class java.lang.String] 
------获取属性值
 [18122335634] 
------设置属性值
 [Person{name='柏竹', age=20, phoneNumber=18122334455}] 

*/

# 方法

java.lang.reflect.Method 反射应用的 方法 对象

通过类的方法访问方法,将返回Method类型对象,每个Method对象代表一个方法

获取类的方法

返回 方法 说明
Method ==getMethod(String name , Class<?>····parameterTypes)== 获取指定方法
Method ==getDeclaredMethod(String name , Class<?>····parameterTypes)== 获取所有权限的 指定方法
Method[] ==ethods()== 获取所有权限的指定方法
Method[] ==getDeclaredMethods()== 获取 所有权限的全部方法,按声明顺序排列

Method类 常用方法 (获取更多方法自行API)

返回 方法 说明
String ==getName()== 获取方法名称
Class[] ==getParameterTypes()== 获取参数的类型
Class ==getReturnType()== 获取方法返回的类型
Class[] ==getExceptionTypes()== 返回方法抛出的异常类型
Object ==invoke(Object obj , Object ... args)== 指定参数args指定obj方法(obj 类/方法)?
Boolean ==isVarArgs()== 带有可变数量的参数,则true
int ==getModifiers()== 获取方法的修饰符(呈现形式整数)
void ==setAccessible(boolean bool)== 是否无视访问权限检查
package com.sans.method;

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

public class Test {
    public static void main(String[] args) throws Exception {
        
        Class<Person> aClass = (Class<Person>) Class.forName("com.sans.method.Person");
        //获取 类构造方法
        Constructor<Person> constructor = aClass.getConstructor(String.class , int.class);
        //获取 类实例的对象
        Object o = constructor.newInstance("柏竹" , 20);
        
        /*No.1
         * 直接获取
         * */
        Method setName = aClass.getMethod("setName" , String.class);
        Method getName = aClass.getMethod("getName");
        System.out.println(setName);
        System.out.println(getName);
        
        System.out.println("===================");
        
        /*No.2
         *   获取所有权限 获取setAge()方法
         * */
        Method setAge = aClass.getDeclaredMethod("setAge", int.class);
        System.out.println(setAge);
        
        System.out.println("===================");
        
        /*No.3
         *   获取所有方法
         * */
        Method[] methods = aClass.getDeclaredMethods();
        //无视权限进行测试使用
        for (Method tmp : methods) {
            System.out.println(tmp);
        }
        
        System.out.println("===================");
        
        /*
         * 方法使用
         * */
        System.out.println("\n-----模拟使用方法");
        //获取 该方法所有权限
        setAge.setAccessible(true);
        System.out.println("使用前 : "+o);
        setAge.invoke(o , 24);
        System.out.println("使用后 : "+o);
        
        System.out.println("\n-----获取方法名");
        System.out.println(" ["+setAge.getName()+"] ");
        
        System.out.println("\n-----获取方法参数类型");
        Method test = aClass.getMethod("test", String.class, int[].class);
        for (Class tmp : test.getParameterTypes()) {
            System.out.println(" ["+tmp+"] ");
        }
        
        System.out.println("\n-----获取方法返回类型");
        System.out.println(" ["+getName.getReturnType()+"] ");
        
    }
}

class Person{
    String name;
    int age;
    
    public Person() {
    }
    
    public Person(String name , int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    /**
     * 私有修饰
     * @param age
     */
    private void setAge(int age) {
        this.age = age;
    }
    
    public void test(String str , int...numAll){
        System.out.println("参数方法测试");
    }
    
    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

/*

public void com.sans.method.Person.setName(java.lang.String)
public java.lang.String com.sans.method.Person.getName()
===================
private void com.sans.method.Person.setAge(int)
===================
private void com.sans.method.Person.setAge(int)
public int com.sans.method.Person.getAge()
public java.lang.String com.sans.method.Person.toString()
public java.lang.String com.sans.method.Person.getName()
public void com.sans.method.Person.setName(java.lang.String)
public void com.sans.method.Person.test(java.lang.String,int[])
===================

-----模拟使用方法
使用前 : person{name='柏竹', age=20}
使用后 : person{name='柏竹', age=24}

-----获取方法名
 [setAge] 

-----获取方法参数类型
 [class java.lang.String] 
 [class [I] 

-----获取方法返回类型
 [class java.lang.String] 

*/

# 注解

java.text.Annotation 类型未了解,可前去网址了解:点击了解 (opens new window)

访问的前提注解要有该注解@Retention(RetentionPolicy.RUNTIME)

获取注解

返回 方法 说明
Annotation ==getAnnotation(Class annotationClass)== 获取指定的Annotation,不存在则返回null
Annotation[] ==getAnnotations()== 获取所有的Annotation
package kkb;

import java.lang.reflect.Field;

public class Demo {
    public static void main(String[] args) throws Exception {
        Class<Book> aClass = (Class<Book>) Class.forName("kkb.Book");
        
        //通过反射 表详细
        MyAnnotationTable at = aClass.getAnnotation(MyAnnotationTable.class);
        String value = at.tableName();
        System.out.println("表名 : " + value);
        
        //属性
        for (Field tmp : aClass.getDeclaredFields()) {
            MyAnnotationField af = tmp.getAnnotation(MyAnnotationField.class);
            System.out.println(tmp.getName() +
                    "属性 , 对应数据库中的字段为 : "+ af.name()+
                    " , 数据类型 : "+af.type()+
                    " , 数据长度 : "+af.length()
            );
        }
    }
}
/*

表名 : test_Book
id属性 , 对应数据库中的字段为 : id , 数据类型 : int , 数据长度 : 20
name属性 , 对应数据库中的字段为 : name , 数据类型 : varchar , 数据长度 : 50
info属性 , 对应数据库中的字段为 : info , 数据类型 : varchar , 数据长度 : 200

*/

# Book类

package kkb;

import java.util.Objects;

/**
 * @Author: 柏竹
 * @Description: 一个简洁主义...
 * @Date_Created_in: 2021-03-06 16:44
 * @Modified_By:
 * @Project: 数据
 */
@MyAnnotationTable(tableName = "test_Book")
public class Book {
    @MyAnnotationField(name = "id" , type = "int" , length = 20)
    private int id;
    @MyAnnotationField(name = "name" , type = "varchar" , length = 50)
    private String name;
    @MyAnnotationField(name = "info" , type = "varchar" , length = 200)
    private String info;
    
    public Book() {
    }
    
    public Book(int id , String name , String info) {
        this.id = id;
        this.name = name;
        this.info = info;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return id == book.id &&
                Objects.equals(name , book.name) &&
                Objects.equals(info , book.info);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id , name , info);
    }
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getInfo() {
        return info;
    }
    
    public void setInfo(String info) {
        this.info = info;
    }
    
    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}

# 注解MyAnnotationField

package kkb;

import java.lang.annotation.*;

/**
 * @Author: 柏竹
 * @Description: 一个简洁主义...
 * @Date_Created_in: 2021-03-06 16:34
 * @Modified_By:
 * @Project: 字段
 */
@Inherited
@Documented
@Target (ElementType.FIELD)
@Retention (RetentionPolicy.RUNTIME)
public @interface MyAnnotationField {
    /**
     * 列名
     * @return
     */
    String name();
    /**
     * 类型
     * @return
     */
    String type();
    /**
     * 数据长度
     * @return
     */
    int length();
}

# 注解MyAnnotationTable

package kkb;

import java.lang.annotation.*;

/**
 * @Author: 柏竹
 * @Description: 一个简洁主义...
 * @Date_Created_in: 2021-03-06 16:32
 * @Modified_By:
 * @Project: 表名
 */
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationTable {
    /**
     * 表名称
     * @return
     */
    String tableName();
}

# 加载配置文件

给项目添加根路径: 项目右键 -> 新建文件夹 -> 设置名 source -> 右键创建的文件夹 -> 标记项目为 -> 资源 根 即可

一般类加载器 加载资源文件默认是src路径下的文件,但是当项目存在 资源根 加载文件这就是该文件夹设置的根!

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;

public class Demo {
    public static void main(String[] args) throws IOException {
        InputStream is = Demo.class.getClassLoader().getResourceAsStream("test.txt");
        BufferedReader br = null;
        if (is != null) {
            br = new BufferedReader(new InputStreamReader(is));
        }
        if (br != null){
            System.out.println(br.readLine());
            br.close();
        }
    }
}
#Java
上次更新: 2023/03/12, 00:43:49

← Java I/O 枚举→

最近更新
01
HTTPS自动续签
10-21
02
博客搭建-简化版(脚本)
10-20
03
ruoyi-vue-plus-部署篇
07-13
更多文章>
Theme by Vdoing | Copyright © 2019-2024 | 桂ICP备2022009417号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式