总结
这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!
某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!
成功只会留给那些有准备的人!
System.out.println("无参构造方法~");
}
public MyClass(String id) {
System.out.println("有参构造方法~");
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@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动态代理**
>
### 最后如何让自己一步步成为技术专家
说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。
当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。
**推荐一份Java架构之路必备的学习笔记,内容相当全面!!!**
![](https://img-blog.csdnimg.cn/img_convert/34e075bcb22e222d9147c14283157f9d.webp?x-oss-process=image/format,png)
成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。
你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!
进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!
**【关注】+【转发】+【点赞】支持我!创作不易!**
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**
**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**
t.UserDao
代理对象信息:class com.sun.proxy.$Proxy2
添加数据前:手动开启事务
添加数据
添加数据后:手动提交事务
cglib动态代理
最后如何让自己一步步成为技术专家
说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。
当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。
推荐一份Java架构之路必备的学习笔记,内容相当全面!!!
[外链图片转存中…(img-jzYe58Y6-1714854571718)]
成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。
你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!
进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!
【关注】+【转发】+【点赞】支持我!创作不易!