2024年Java--反射机制原理、几种Class获取方式及应用场景_xx(3),阿里云大师深入拆解Java虚拟机

总结

三个工作日收到了offer,头条面试体验还是很棒的,这次的头条面试好像每面技术都问了我算法,然后就是中间件、MySQL、Redis、Kafka、网络等等。

  • 第一个是算法

关于算法,我觉得最好的是刷题,作死的刷的,多做多练习,加上自己的理解,还是比较容易拿下的。

而且,我貌似是将《算法刷题LeetCode中文版》、《算法的乐趣》大概都过了一遍,尤其是这本

《算法刷题LeetCode中文版》总共有15个章节:编程技巧、线性表、字符串、栈和队列、树、排序、查找、暴力枚举法、广度优先搜索、深度优先搜索、分治法、贪心法、动态规划、图、细节实现题

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

《算法的乐趣》共有23个章节:

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)

基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

@Override
public String toString() {
    return "MyClass{" +
            "id='" + id + '\'' +
            '}';
}

}


**(2)单元测试类:**  
 通过`@Test`注解对三种方式分别进行单元测试,再对这三种方式的组合进行单元测试~



package com.justin.java.lang;

import org.junit.Test;

/**
* @program: Jdk1.8Test
* @description: Java反射机制中获取类的Class实例对象的常见三种方式及区别对比
* @author: JustinQin
* @create: 2021/8/22 15:04
* @version: v1.0.0
**/
public class MyClassTest {

@Test
public void test1() {
    System.out.println("一、MyClass.class方式=========");
    Class<?> class1 = MyClass.class;
}

@Test
public void test2() throws ClassNotFoundException {
    System.out.println("二、Class.forName方式=========");
    Class class2 = Class.forName("com.justin.java.lang.MyClass");
}


@Test
public void test3() {
    System.out.println("三、new MyClass().getClass方式=========");
    Class class3 = new MyClass().getClass();
}

@Test
public void test12() throws ClassNotFoundException {
    System.out.println("一、MyClass.class方式=========");
    Class<?> class1 = MyClass.class;
    System.out.println("二、Class.forName方式=========");
    Class class2 = Class.forName("com.justin.java.lang.MyClass");
}

@Test
public void test13() {
    System.out.println("一、MyClass.class方式=========");
    Class<?> class1 = MyClass.class;
    System.out.println("三、new MyClass().getClass方式=========");
    Class class3 = new MyClass().getClass();
}

@Test
public void test23() throws ClassNotFoundException {
    System.out.println("二、Class.forName方式=========");
    Class class2 = Class.forName("com.justin.java.lang.MyClass");
    System.out.println("三、new MyClass().getClass方式=========");
    Class class3 = new MyClass().getClass();
}

@Test
public void test() throws ClassNotFoundException {
    System.out.println("四、三种方式内存地址比较=========");
    Class<?> class1 = MyClass.class;
    Class class2 = Class.forName("com.justin.java.lang.MyClass");
    Class class3 = new MyClass().getClass();
    System.out.println("比较结果=========");
    System.out.println("MyClass.class和Class.forName内存地址比较是否相同:" + (class1 == class2));
    System.out.println("MyClass.class和new MyClass().getClass内存地址比较是否相同:" + (class1 == class3));
    System.out.println("Class.forName和new MyClass().getClass内存地址比较是否相同:" + (class2 == class3));
}

}


**逐个执行单元,得出测试结果为:**



* test1()方法
一、MyClass.class方式=========

* test2()方法
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021

* test3()方法
三、new MyClass().getClass方式=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~

* test12()方法
一、MyClass.class方式=========
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021

* test13()方法
一、MyClass.class方式=========
三、new MyClass().getClass方式=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~

* test23()方法
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021
三、new MyClass().getClass方式=========
动态代码块~
无参构造方法~

* test()方法
四、三种方式内存地址比较=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~
比较结果=========
MyClass.class和Class.forName内存地址比较是否相同:true
MyClass.class和new MyClass().getClass内存地址比较是否相同:true
Class.forName和new MyClass().getClass内存地址比较是否相同:true


**通过`test1`、`test2`、`test3`的测试结果验证了`2.1 三种方式及区别`中黄色标记部分的区别说明,即:**


* `MyClass.class`不会做任何类的初始化工作
* `Class.forName`会进行类的静态初始化工作
* `new MyClass().getClass`静态初始化和非静态初始化工作都会进行
* 使用这三种方式任意一种最终在JVM加载到内存中都会是`内存地址相同`的


**而`test23`组合得到的测试结果,说明`静态代码块只会被加载一次`~**


**讲了这么多,除了知道基本原理和基本使用之外,更重要的还是要知道它的一些比较实际的`应用场景`,往下介绍~**


## 💥三、Java反射机制的应用场景有哪些?


![在这里插入图片描述](https://img-blog.csdnimg.cn/3a790d5d55bf44b1854eedb953c583d3.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0p1c3RpblFpbg==,size_16,color_FFFFFF,t_70)


### 🎶3.1 应用场景


* `工厂模式`中的简单工厂模式优化
* `代理模式`中的动态代理方式实现
* `Java JDBC`数据库操作


### 🎧3.2 简单工厂模式优化


#### 📢3.2.1 什么是简单工厂模式?



> 
> Java中主要有23种设计模式,其中工厂模式就是其中一种,而简单工厂模式,顾名思义,也是属于工厂模式中的一种,只不过比较简单。简单工厂模式也可以叫做静态方法模式(因为工厂类一般都是在内部定义了一个静态方法)。  
>  从现实生活角度来理解的话,工厂是专门负责生产产品的,同样在设计模式中,简单工厂模式我们可以理解为专门负责生产对象的一个类,称为“工厂类”。
> 
> 
> 


#### 🎹3.2.2 简单工厂模式有什么用?



> 
> 简单工厂模式通过创建一个对应的工厂类,将`类实例化的操作`与`使用对象的操作`进行分开,让使用者不用知道具体参数就可以实例化出所需要的`具体产品`类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产的细节~
> 
> 
> 


#### 🎸3.2.3 如何实现简单工程模式?



> 
> 实现简单工程模式的核心是创建一个`工厂类`,并且在内部定义了一个静态方法,传入不同的`参数标识`通过`switch`进行分组,通过`new`实例化创建不同的子类对象返回~
> 
> 
> 


**实现例子:**


**步骤1:创建抽象产品类**



public interface Product {
public abstract void show();
}


**步骤2:创建具体产品类:**



public class ProductA implements Product {
@Override
public void show() {
System.out.println(“生产了产品A”);
}
}
public class ProductB implements Product {
@Override
public void show() {
System.out.println(“生产了产品B”);
}
}

public class ProductC implements Product {
@Override
public void show() {
System.out.println(“生产了产品C”);
}
}


**步骤3:创建简单工厂类**



public class SimpleFactory {
/**
* 实现简单工厂模式
* @param pName 产品标识
* @return 返回具体的产品
*/
public static Product createProduct(String pName){
switch (pName){
case “A”:
return new ProductA();
case “B”:
return new ProductB();
case “C”:
return new ProductC();
default:
return null;
}
}
}


**步骤4:调用简单工厂类**



public class SimpleFactoryTest {
public static void main(String[] args) {
try {
SimpleFactory.createProduct(“A”).show();
} catch (NullPointerException e) {
System.out.println(“没有A这款产品,无法生产~”);
}
try {
SimpleFactory.createProduct(“B”).show();
} catch (NullPointerException e) {
System.out.println(“没有B这款产品,无法生产~”);
}
try {
SimpleFactory.createProduct(“C”).show();
} catch (NullPointerException e) {
System.out.println(“没有C这款产品,无法生产~”);
}
try {
SimpleFactory.createProduct(“D”).show();
} catch (NullPointerException e) {
System.out.println(“没有D这款产品,无法生产~”);
}
}
}


#### 📣3.2.4 简单工厂模式优化


**(1)简单工厂模式弊端**


* 操作成本高:每增加一个接口的子类,必须修改工厂类的逻辑
* 系统复杂性提高:每增加一个接口的子类,都必须向工厂类添加逻辑


这两点弊端从前面的例子`SimpleFactory`工厂类的实现,可以看出`简单工厂模式`中对工厂类`SimpleFactory`的维护成本有点大,因为实际中可能会很频繁的去更新`具体产品类`,每一次变更都需要去修改工厂类,此时就可以利用`Java反射机制`对简单工厂模式进行优化~


**(2)简单工厂模式的优化思路**  
 采用Java反射机制,通过传入`子类全局定名(包名+类名)` 动态的创建不同的`子类对象实例`,从而使得在不增加产品接口子类和修改工厂类的逻辑的情况下还能实现了工厂类对子类实例对象的统一创建~


**(3)简单工厂模式的优化步骤**  
 **步骤1:创建工厂类**  
 采用Java反射机制对工厂类进行优化,主要是将`className`即`子类全局定名(包名+类名)`作为入参,通过`Class.forName`方式获取类的`java.lang.Class`实例对象,再通过`Class`实例对象的`getInstance`方法获取到具体子类的实例对象~



public class Factory {
public static Product getInstance(String className) {
Product realProduct = null;
try {
Class pClass = Class.forName(className);
realProduct = (Product) pClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return realProduct;
}
}


**步骤2:调用工厂类**



public class FactoryTest {
public static void main(String[] args) {
try {
Product productA = Factory.getInstance(“com.justin.java.lang.ProductA”);
productA.show();
} catch (NullPointerException e) {
System.out.println(“没有A这款产品,无法生产~”);
}

    try {
        Product productB = Factory.getInstance("com.justin.java.lang.ProductB");
        productB.show();
    } catch (NullPointerException e) {
        System.out.println("没有B这款产品,无法生产~");
    }

    try {
        Product productC = Factory.getInstance("com.justin.java.lang.ProductC");
        productC.show();
    } catch (NullPointerException e) {
        System.out.println("没有C这款产品,无法生产~");
    }

    try {
        Product productD = Factory.getInstance("com.justin.java.lang.ProductD");
        productD.show();
    } catch (Exception e) {
        System.out.println("没有D这款产品,无法生产~");
    }


}

}


**优化结果:**



> 
> 使用`Java反射机制`优化简单工厂模式后,可以看到,不论`具体产品类`更新多频繁,都不需要再修改`工厂类`,从而解决了普通简单工厂模式`操作成本高`和`系统复杂性高`的问题~
> 
> 
> 


#### 🎵3.2.5 简单工厂模式再次优化


**(1)再次优化背景**



> 
> 简单工厂模式的工厂类采用`Java反射机制`进行优化后,此时的仍然存在这样一个问题,`子类的全局定名(包名+类名)`是写死的,但是实际上开发者在写代码时是很难提前预知所有的`子类的全局定名(包名+类名)`的,因此需要进行二次优化~
> 
> 
> 


**(2)再次优化实现思路**



> 
> 通过`配置文件`方式,统一定义`类名对应全局定名(包名+类名)`,将配置文件存放到资源目录下,程序运行时通过`ClassLoader`类加载器动态获取到`配置文件`中定义的子类的全局定名~
> 
> 
> 


**(3)再次优化实现步骤**


**再次优化步骤1:相关优化与第一次优化保持不变~**


**再次优化步骤2:配置`类名对应全局定名(包名+类名)`**  
 创建属性配置文件`Product.properties`



//产品抽象类Product相关子类的全局定名(包名+类名)定义
ProductA = com.justin.java.lang.ProductA
ProductB = com.justin.java.lang.ProductB
ProductC = com.justin.java.lang.ProductC


注意:将`Product.properties`需要存放在`src/main/resources`资源目录下,若资源目录不存在则需要手动创建~


**再次优化步骤3:修改调用工厂类**



public class FactoryTest {
@Test
public void test() throws IOException {
ClassLoader classLoader = this.getClass().getClassLoader();
Properties prop = new Properties();
prop.load(classLoader.getResourceAsStream(“Product.properties”));

    String className = "";
    try {
        className = prop.getProperty("ProductA");
        Product productA = Factory.getInstance(className);
        productA.show();
    } catch (NullPointerException e) {
        System.out.println("没有A这款产品,无法生产~");
    }

    try {
        className = prop.getProperty("ProductB");
        Product productA = Factory.getInstance(className);
        productA.show();
    } catch (NullPointerException e) {
        System.out.println("没有B这款产品,无法生产~");
    }

    try {
        className = prop.getProperty("ProductC");
        Product productA = Factory.getInstance(className);
        productA.show();
    } catch (NullPointerException e) {
        System.out.println("没有C这款产品,无法生产~");
    }
}

}


**运行结果:**



生产了产品A
生产了产品B
生产了产品C


### 📀3.3 代理模式中的动态代理实现


![在这里插入图片描述](https://img-blog.csdnimg.cn/70d9c6bbc83448aab1a2a755fcc75987.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0p1c3RpblFpbg==,size_16,color_FFFFFF,t_70)


#### 🔊3.3.1 什么是代理模式?



> 
> `代理(Proxy)模式`是一种`设计模式`,通过`代理对象`来访问`目标对象`,还可以在不修改`目标对象`的情况下,对`代理对象`进行拓展,增强`目标对象`的功能~
> 
> 
> 


**什么?还是不太理解?**



> 
> 更通俗一点的说代理模式,就是想做某件事(`买火车票`),`自己`能买(直接去`火车站`买),却委托别人去买(没空还是`代理点`买吧),还可以让别人帮自己做其他事(订好酒店)~
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/bf70d190a42f4b74af640c330acbbc9b.png)


**代理模式又分为静态代理、动态代理,往下介绍~**


#### 💥3.3.2 什么是静态代理?



> 
> (1)`静态代理`属于`代理模式`的一种代理方式,需要`代理对象`和`目标对象`实现相同的接口  
>  (2)`静态代理`的代理类是由程序员编写源码,编译后即可获取到代理类的class字节码文件,也就是在`程序运行前`就已经得到实际的代理类class字节码文件了
> 
> 
> 


#### 🎶3.3.2 什么是动态代理?


**动态代理**



> 
> (1)`动态代理`也属于`代理模式`的一种代理方式,不过只需要`目标对象`实现接口,`代理对象`不需要实现接口~  
>  (2)`动态代理`的代理类编译后是没有class字节码文件的,而是在运行时利用`Java反射机制`动态的生成代理类的class字节码文件~
> 
> 
> 


动态代理最常用的是`JDK原生动态代理`和`cglib动态代理`,往下介绍~


**JDK 原生动态代理**


JDK 原生动态代理,主要利用了`JDK API`的  
 `java.lang.reflect.Proxy`和`java.lang.relfect.InnvocationHandler` 这两个类来实现~


通过`java.lang.reflect.Proxy`代理类的`newProxyInstance`方法,传递3个参数,分别是:  
 `目标对象的加载器` 通过`MyClass.getClass().getClassLoader`方式获取  
 `目标对象的实现接口类型` 通过`Object.getClass().getInterfaces()`方式获取  
 `InnvocationHandler事件处理器` 通过`new`实例化对象并重写`invoke`方法方式获取


**例子:**


**用户接口类`IUserDao`**



public interface IUserDao {
//添加数据
public void insert();
}


**目标对象类`UserDao`**



/**
* @program: DataStructures
* @description:
* @author: JustinQin
* @create: 2021/8/23 23:32
* @version: v1.0.0
**/
public class UserDao implements IUserDao{

@Override
public void insert() {
    System.out.println("添加数据");
}

}


**动态代理类`UserProxy`**



/**
* @program: Jdk1.8Test
* @description: 动态代理类
* @author: JustinQin
* @create: 2021/8/23 23:31
* @version: v1.0.0
**/
public class UserProxy {
private Object target; //目标对象

public UserProxy(Object target) {
    this.target = target;
}

/\*\*

* 利用JDK API获取到代理对象
* @return
*/
public Object getProxyInstance() {
//目标对象的加载器
ClassLoader loader = target.getClass().getClassLoader();

    //目标对象的实现接口类型
    Class<?>[] interfaces = target.getClass().getInterfaces();

    //InnvocationHandler事件处理器实例对象
    InvocationHandler h = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("添加数据前:手动开启事务");
            // 执行目标对象方法
            Object value = method.invoke(target, args);
            System.out.println("添加数据后:手动提交事务");
            return null;
        }
    };
    //传入3个参数,创建代理类的实例对象,并返回
    return Proxy.newProxyInstance(loader, interfaces,h);
}

}


**动态代理单元测试类**



/**
* @program: 动态代理单元测试类
* @description:
* @author: JustinQin
* @create: 2021/8/23 23:42
* @version: v1.0.0
**/
public class UserProxyTest {
@Test
public void test() {
IUserDao target = new UserDao();
System.out.println(“目标对象信息:” + target.getClass());
//获取代理类实例对象
IUserDao proxy = (IUserDao) new UserProxy(target).getProxyInstance();
System.out.println(“代理对象信息:” + proxy.getClass());
//执行代理方法
proxy.insert();
}
}


**单元测试执行结果**



目标对象信息:class com.justin.java.reflect.UserDao
代理对象信息:class com.sun.proxy.$Proxy2
添加数据前:手动开启事务
添加数据
添加数据后:手动提交事务


**cglib动态代理**



> 
> `cglib (Code Generation Library )`是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
> 
> 
> 


`Spring AOP`结合了`cglib动态代理`和`JDK原生动态代理`来实现,这里不过多介绍,有兴趣小伙伴可以查阅资料学习下~


#### 🎧3.3.3 动态代理中如何利用Java反射机制?



> 
> JDK原生动态代理中,获取代理示例对象过程中,获取目标对象的类加载器,通过`target.getClass().getClassLoader(`获取到目标对象的类加载器,`target.getClass()`方式获取目标对象的Class实例对象使用的就是Java反射机制来实现的~
> 


# Docker步步实践

**目录文档:**

![](https://img-blog.csdnimg.cn/img_convert/ad28f9ed3a1e2b3408a7acf79cde99d2.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/6685c793744d9c9b95cfae8808a1afae.webp?x-oss-process=image/format,png)

**①Docker简介**

**②基本概念**

**③安装Docker**

![](https://img-blog.csdnimg.cn/img_convert/73fdd198da8760780cf5c5107c51fc28.webp?x-oss-process=image/format,png)

**④使用镜像:**

![](https://img-blog.csdnimg.cn/img_convert/15756908bea0cd806fd87377ed06da20.webp?x-oss-process=image/format,png)

**⑤操作容器:**

![](https://img-blog.csdnimg.cn/img_convert/ff8da7a66704eb3b70358ffbe83c0735.webp?x-oss-process=image/format,png)

**⑥访问仓库:**

![](https://img-blog.csdnimg.cn/img_convert/499bd2237a9fc7a65cd9b80a4a80b8b7.webp?x-oss-process=image/format,png)

**⑦数据管理:**

![](https://img-blog.csdnimg.cn/img_convert/2de516148149be4a49e2c99cbb4a93b2.webp?x-oss-process=image/format,png)

**⑧使用网络:**

![](https://img-blog.csdnimg.cn/img_convert/829cb053e143646e43867c677e3df225.webp?x-oss-process=image/format,png)

**⑨高级网络配置:**

![](https://img-blog.csdnimg.cn/img_convert/da4158166713efedfc418190b7c00f69.webp?x-oss-process=image/format,png)

**⑩安全:**

![](https://img-blog.csdnimg.cn/img_convert/822bb257f3085beacebd3193eb97369d.webp?x-oss-process=image/format,png)

**⑪底层实现:**

![](https://img-blog.csdnimg.cn/img_convert/118bdd1c1ad58dea055285f49aa6d60a.webp?x-oss-process=image/format,png)

**⑫其他项目:**

![](https://img-blog.csdnimg.cn/img_convert/d818a72f851644869c59ecaa6bfbe45c.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

zclQ-1714840466816)]

**①Docker简介**

**②基本概念**

**③安装Docker**

[外链图片转存中...(img-mBDORUUW-1714840466817)]

**④使用镜像:**

[外链图片转存中...(img-CyfJQbZA-1714840466817)]

**⑤操作容器:**

[外链图片转存中...(img-ktRJkjuO-1714840466817)]

**⑥访问仓库:**

[外链图片转存中...(img-sAgBlU3Y-1714840466818)]

**⑦数据管理:**

[外链图片转存中...(img-Y1cfAujt-1714840466818)]

**⑧使用网络:**

[外链图片转存中...(img-pTqmFNnx-1714840466818)]

**⑨高级网络配置:**

[外链图片转存中...(img-FvLz00J9-1714840466818)]

**⑩安全:**

[外链图片转存中...(img-7Qi6jlps-1714840466819)]

**⑪底层实现:**

[外链图片转存中...(img-pUrAFedq-1714840466819)]

**⑫其他项目:**

[外链图片转存中...(img-Ms6S7djo-1714840466819)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值