面试总结之Java基础

1、反射

1.1、概述

  • 反射:加载类(通过反射将类的字节码文件加载到内存中),并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)
  • 反射需要掌握的内容:
    1)记载类,获取类的字节码:Class对象
    2)获取类的构造器:Constructor对象
    3)获取类的成员变量:Field对象
    4)获取类的成员方法:Method对象

1.2、获取类的字节码

面试总结之Java基础_第1张图片

  • 获取Class对象可以通过如下三种方式:
    1)Class c1 = 类名.class
    2)调用Class类提供的forName方法:public static Class forName(String className)
    3)Object提供的getClass方法: public final native Class getClass();
  • 示例代码:
        Class<Person> p1 = Person.class;
        System.out.println(p1.getName());       // 类的全路径
        System.out.println(p1.getSimpleName()); // 类简称: Person

        Class<?> p2 = Class.forName("com.example.interviewStudy.reflect.Person");  // forName方法中的参数需要是类的全路径
        System.out.println(p1 == p2);   // true, p1和p2指向同一个对象

        Person person = new Person();
        Class p3 = person.getClass();
        System.out.println(p1 == p3);  // true

1.3、获取类的构造器

面试总结之Java基础_第2张图片

  • 获取类构造器的作用:
    面试总结之Java基础_第3张图片
  • 示例代码:
        Class p1 = Person.class;
        Constructor<?>[] con1 = p1.getConstructors();
        for (Constructor<?> c : con1) {
            System.out.println(c.getName() +"<--->获取构造器参数总个数:" +c.getParameterCount());
        }

        System.out.println("获取所有构造器(包括私有的) ==>");
        Constructor<?>[] con2 = p1.getDeclaredConstructors();
        for (Constructor<?> c : con2) {
            System.out.println(c.getName() +"<--->参数总个数:" +c.getParameterCount());
        }

        System.out.println("根据传入参数的类型 调用指定的构造器来创建对象 ==》");
        Constructor  publicPerson = p1.getConstructor(Long.class, String.class);
        Person person1 = (Person) publicPerson.newInstance(10189L, "张三");
        System.out.println(person1);

        System.out.println("根据传入参数的类型 获取指定的 私有构造器 ==》");
        Constructor<Person> privatePerson = p1.getDeclaredConstructor(String.class);
        privatePerson.setAccessible(true);   // 暴力反射,禁止检查访问权限,如果不禁止权限,则会报IllegalAccessException
        
        // 调用无参构造器(将Person类中无参构造改为private修饰)
        Person person2 = privatePerson.newInstance();
        System.out.println(person2);
        // 调用传入pname属性的构造器
        Person person3 = privatePerson.newInstance("张小敬");
        System.out.println(person3);

注意:如果不调用构造器对象的setAccessible,不禁止检查访问权限,就会报如下的
在这里插入图片描述

1.4、获取类的成员变量

面试总结之Java基础_第4张图片
为测试获取Person类的私有属性,需要将Person的pname属性改为private修饰

  • 示例代码:
        Class p1 = Person.class;
        //  p1.getConstructors(): 只能获取public修饰的属性
        //  p1.getDeclaredFields(): 能获取所有的属性
        Field[] fields = p1.getDeclaredFields();
        // 遍历Person类中的成员变量
        for (Field f : fields) {
            System.out.println(f.getName() + "<--->" + f.getType());
        }
        
        // p1.getField(): 只能指定获取public修饰的成员变量
        Field pname = p1.getDeclaredField("pname");
        Person person = new Person();
        // 给private修饰的成员变量赋值,需要禁止检查访问权限
        pname.setAccessible(true);
        pname.set(person,"朱重八");
        System.out.println("person = " + person);

        // 获取成员变量
        // 从person 对象中获取pname
        String name = (String) pname.get(person);
        System.out.println(name);

1.5、获取类的成员方法

面试总结之Java基础_第5张图片

  • 示例代码:
        Class p1 = Person.class;
        Method[] methods = p1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "<-->" + method.getParameterCount() + "<-->访问修饰符:" + method.getModifiers());
        }

        Person person = new Person();
        Method method1 = p1.getDeclaredMethod("printInfo", Long.class, String.class, Integer.class);
        // 暴力反射
        method1.setAccessible(true);

        // 执行包含参数且有返回值的的成员方法,res1就是原本调用方法的返回值
        Object res1 = method1.invoke(person, Long.valueOf(100897), "盛明兰", 26);
        System.out.println(res1);

        // 执行包含无参且没有返回值的成员方法,其原本方法的返回值就是null
        Method method2 = p1.getDeclaredMethod("message");
        method2.setAccessible(true);
        Object res2 = method2.invoke(person);
        System.out.println(res2);

1.6、反射的作用和应用场景

1)反射的作用

  • 可以得到类全部的属性、构造器、成员变量、成员方法后进行其他操作
  • 破坏封装性
  • 适合实现Java框架,基本上主流的框架都会基于反射设计出一些通用的功能

2)反射的应用场景

  • 需求:

实现一个简易版的持久化框架,对于任意一个对象,该框架都可以将对象的字段名和对应值,保存到txt格式的文本中

  • 实现步骤:
    1)定义一个方法,可以接收任意对象
    2)每接收到一个对象,都通过反射获取到该对象的Class对象,根据Class对象来获取全部的成员变量
    3)遍历所有的成员变量并获取到该变量在对象中的具体值
    4)将变量和对应的值通过IO流的方式输出到文本中
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class ObjectFrame {
    public static void saveEntity(Object entity) throws IllegalAccessException, FileNotFoundException {
        // append追加文本的形式将数据输出data.txt文本中
        PrintStream printStream = new PrintStream(new FileOutputStream("D:\\data1.txt",true));
        Class clazz = entity.getClass();

        String simpleName = clazz.getSimpleName();
        printStream.println("=========" + simpleName + "==========");
        // 获取entity对象所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String dataKey = field.getName();
            String dataValue = field.get(entity) + "";
            printStream.println(dataKey + " = " + dataValue);
        }
        printStream.close();
    }
}
  • 测试:
    public static void main(String[] args) throws FileNotFoundException, IllegalAccessException {
        Person p1 = new Person(1009876L,"张小敬",28);
        Person p2 = new Person(2091823L,"雷东宝",32);
        Person p3 = new Person(2091823L,"宋运辉",26);

        ObjectFrame.saveEntity(p1);
        ObjectFrame.saveEntity(p2);
        ObjectFrame.saveEntity(p3);


        Teacher t1 = new Teacher(101L,"李知恩","语文");
        Teacher t2 = new Teacher(104L,"高启强","数学");

        ObjectFrame.saveEntity(t1);
        ObjectFrame.saveEntity(t2);
    }

此外,反射在实际项目中还可以实现日志输出管理、公共字段/属性的自动填充(createUser、createTime、updateUser、updateTime)、事务、权限控制等,这需要结合AOP来实现

1.7、反射的优缺点

  • 反射的优点:
    • 在运行时可以获取任意类的属性、构造方法、成员方法,以及动态调用这些方法和修改属性
    • 提高代码的复用率,如:在动态代理的场景中,使用动态生成的代理类来提升代码的复用性; 在Spring框架中,用反射来实例化Bean对象,用反射实现对象的属性拷贝的BeanUtils.copyProperties()方法;使用反射来自定义注解;
      注意:使用BeanUtils.copyProperties()来完成对象属性的拷贝需要注意泛型擦除的问题
  • 反射的缺点:
    • 反射会涉及动态类型的解析,JVM无法对这些代码进行优化,导致性能比非反射调用低
    • 反射可以绕过一些限制访问的属性和方法(如使用setAccessible方法来禁止检查访问权限),会破坏代码的封装性

2、注解

2.1、概述

面试总结之Java基础_第6张图片

2.2、自定义注解

面试总结之Java基础_第7张图片

2.3、注解的解析

面试总结之Java基础_第8张图片

2.4、应用场景

注解被用错位置在编译期间就会报错

  • 元注解:指修饰注解的注解
    面试总结之Java基础_第9张图片
    面试总结之Java基础_第10张图片

面试总结之Java基础_第11张图片

面试总结之Java基础_第12张图片

面试总结之Java基础_第13张图片

3、单例模式

3.1、什么是单例模式

  • 单例模式属于创建型模式,提供了一种创建对象的最佳方式,是指仅在内存中只创建一次对象的设计模式,在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象
  • 单例模式的优点:提升使用对象的效率,又可以让类自行把控实例的实现细节,对外部业务代码不产生任何影响
  • 单例模式的应用场景:HttpClient、Spring中的对象、打印日志的logger、数据库连接池对象

3.2、单例模式原则

  • 构造器私有(防止类被通过常规方法实例化)
  • 以静态方法或枚举返回实例(保证实例的唯一性)
  • 确保实例只有一个,尤其是在多线程环境下(保证在创建实例时的线程安全)
  • 确保反序列化时不会重新构建对象(在有序列化、反序列化的场景下防止单例被莫名破坏,造成未考虑到的后果)

面试总结之Java基础_第14张图片

3.3、Java实现单例模式有哪些方式

  • 概括起来,实现单例需要关注如下几点:
    • 构造函数需要是private访问权限,以此避免外部通过new创建实例
    • 考虑是否支持延迟加载
    • 考虑对象创建时的线程安全问题
    • 考虑获取实例的getInstance()方法性能是否够高(是否加锁)
1)懒汉式单例
  • 懒汉式单例:需要实例对象时,调用getInstance方法才去创建对象(适用于对象比较大的情况,不希望类被加载时就实例化从而拖慢程序的启动时长)
/***
 * 懒汉模式
 */
public class LazySingleton {

    private static LazySingleton instance;

    private LazySingleton(){ }

    // 如果不加锁,则不能保证线程安全
    // 锁的粒度较粗,同JDK1.0中的HashTable的设计,故HashTable不建议使用
    public static synchronized LazySingleton getInstance(){
        if (instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}
2)饿汉式单例
  • 饿汉式单例:可保证线程安全,不管需不需要单例对象instance ,都在类加载时进行初始化,会浪费内存资源,基于ClassLoader机制避免了多线程的同步问题
public class HungrySingleton {
    
    private static HungrySingleton instance = new HungrySingleton();
    
    private HungrySingleton(){ }
    
    public static HungrySingleton getInstance(){
        return instance;
    }
}
3)双重锁式单例
// DCL: Double Check Lock,双重锁校验
public class DCLSingleton {

     // 基于volatile来保证可见性、有序性,禁止指令重排
    private static volatile DCLSingleton instance;

    private DCLSingleton() {
    }

    public static DCLSingleton getInstance() {
        if (instance == null) {  // 第一次检查
            // 加锁保证线程安全
            synchronized (DCLSingleton.class) {
                if (instance == null) {  // 再次检查防止同时进入synchronized 
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}
  • 为什么双重锁式单例中instance要有volatile修饰?
    在JVM执行过程中,instance实例的创建主要分为三步:
    1)在堆中为对象分配内存空间
    2)初始化对象(为属性赋值)
    3)将实例对象指向堆内存中的内存空间
    但由于JVM指令重排的优化,可能会出现
    同时,volatile是轻量级的同步机制,可及时刷新内存,但volatile不能保证原子性,故需要配合synchronized来保证其可靠性
4)静态内部类式单例
  • 基于延迟加载的方式,静态内部类实现单例模式:
/**
 *  静态内部类实现单例,线程安全
 * StaticInnerSingleton类被装载了,instance不一定被实例化,因为SingletonHolder类没有被主动使用
 * 当执行getInstance时,才会显示地装载SingletonHolder类,实例化instance
 */
public class StaticInnerSingleton {

    private StaticInnerSingleton() {
    }

    private static class SingletonHolder {
        private static final StaticInnerSingleton instance = new StaticInnerSingleton();
    }

    public static final StaticInnerSingleton getInstance() {
        return SingletonHolder.instance;
    }
}
5)枚举式单例
public enum EnumSingleton {

    INSTANCE;

    // 执行业务代码
    public void doSomeThing(){
        System.out.println("执行业务逻辑....");
    }
}
6)CAS式单例
import java.util.concurrent.atomic.AtomicReference;

public class Singleton {

    priavte static final AtomicReference<Singleton> casInstance = new AtomicReference<>();

    public static final Singleton getInstance() {
        for (; ; ) {
            Singleton singleton = casInstance.get();
            if (singleton != null) return singleton;
            // 如果casInstance为null,则创建单例对象并返回
            casInstance.compareAndSet(null,new Singleton());
            return casInstance.get();
        }
    }
}

3.4、关于设计模式

  • 按照设计模式的应用目的进行分类,设计模式可以分为创建型模式、结构型模式、行为型模式
  • 创建型模式:是对象在创建过程中各种问题和解决方案的总结,包括工厂模式、单例模式、原型模式、构建者模式
  • 结构型模式:是针对软件设计结构的总结,关注于类、对象、组合方式的实践经验,常见的结构型模式有桥接模式、适配器模式、门面模式、装饰者模式、代理模式、享元模式等
  • 行为型模式:是类或对象之间交互、职责划分等角度总结的设计模式,常见的行为型模式有策略模式、观察者模式、迭代器模式、适配器模式、模板方法模式、访问者模式等

面试总结之Java基础_第15张图片

面试总结之Java基础_第16张图片
面试总结之Java基础_第17张图片

面试总结之Java基础_第18张图片

面试总结之Java基础_第19张图片
面试总结之Java基础_第20张图片

面试总结之Java基础_第21张图片

面试总结之Java基础_第22张图片

面试总结之Java基础_第23张图片

面试总结之Java基础_第24张图片

面试总结之Java基础_第25张图片

你可能感兴趣的:(面试,java,设计模式)