Chp18-反射和设计模式
反射
是一种底层技术,通常用于底层框架的编写
类对象
- 类的对象: 是类实例化的结果,可以存在多个
- 类对象: 是类加载的产物, 通常只有一个, 内部存放类的所有信息(包,属性,方法,构造,父类信息,接口信息等)
获取类对象-Class
- 类名.class
Class<类名> 对象名=类名.class
-
对象名.getClass()
Class 对象名=对象名.getClass();
3. Class.forName
~~~java
Class 对象名=Class.forName("类的全限定名");
public static void main(String[] args) throws Exception{
Class<Student> c1 = Student.class;
Student stu = new Student();
Class c2 = stu.getClass();
Class c3 = Class.forName("com.by.entity.Student");
}
常用方法
- newInstance(): 利用类对象中的无参构造构建一个类的实例对象
- Constructor<类名> getDeclaredConstructor(参数列表的类对象1,参数列表的类对象2,…): 获取指定参数列表的有参构造器对象
- newInstance(实参列表): 利用指定有参构造器对象构建类的实例对象
public static void main(String[] args) throws Exception{
Class<Student> c1 = Student.class;
Student stu1 = c1.newInstance();
Class c3 = Class.forName("com.by.entity.Student");
//获取的对像默认为Object类型,需要进行类型强转
Student stu2 = (Student) c3.newInstance();
stu1.setName("张三");
stu2.setName("李四");
System.out.println(stu1);
System.out.println(stu2);
//获取全属性的有参构造器对象
//Constructor<Student> con = c1.getDeclaredConstructor(String.class, int.class, double.class);
Constructor<Student> con = c3.getDeclaredConstructor(String.class, int.class, double.class);
//利用有参构造器对象构建类的实例
Student stu3 = con.newInstance("张三", 20, 99.5);
System.out.println(stu3);
}
反射的优缺点
优点
- 打破封装
- 提升代码扩展性
缺点
- 打破封装
- 降低对象属性的安全性
反射掌握重点
- 类对象和类的对象的区别
- 获取类对象的三种方式
- 通过反射构建类的对象的两种方式
设计模式
是广大程序员们总结的编码套路
单例模式
一个类只能被实例化一个对象
饿汉式
- 无论如何直接创建唯一实例
package com.by.entity;
/**
* 单例-饿汉式
*/
public class ClassA {
/*
private: 保证外界无法直接访问该属性
static: 1. 保证newClassA方法可以访问
2. 使属性成为静态属性,内存中只会存在一份,如此就能保证实例化操作只会执行一次
* */
private static ClassA ca = new ClassA();
/**
* 供外界调用获取该类的唯一实例
* static: 方便外界通过类名直接调用该方法
* @return 当前类的唯一实例对象
*/
public static ClassA newClassA(){
return ca;
}
//构造私有化: 防止外界通过调用构造创建不同对象
private ClassA(){
}
}
缺点:有可能浪费对象空间
懒汉式
- 需要对象时再实例化对象
package com.by.entity;
/**
* 单例-懒汉式
*/
public class ClassB {
/*
private: 保证外界无法直接访问该属性
static: 1. 保证newClassA方法可以访问
2. 使属性成为静态属性,内存中只会存在一份,如此就能保证实例化操作只会执行一次
* */
private static ClassB cb = null;
/**
* 供外界调用获取该类的唯一实例
* static: 方便外界通过类名直接调用该方法
* @return 当前类的唯一实例对象
* synchronized: 同步方法,解决线程安全问题
*/
public static synchronized ClassB newClassB(){
if (cb==null) {//空值判断: 确定是第一次获取实例
cb = new ClassB();
}
return cb;
}
//构造私有化: 防止外界通过调用构造创建不同对象
private ClassB(){
}
}
缺点:线程效率慢
懒汉式-进阶版
- 在懒汉式的基础上,利用二次校验+同步代码块尽可能提高线程效率
package com.by.entity;
/**
* 单例-懒汉式进阶版
*/
public class ClassB {
/*
private: 保证外界无法直接访问该属性
static: 1. 保证newClassB方法可以访问
2. 使属性成为静态属性,内存中只会存在一份,如此就能保证实例化操作只会执行一次
* */
private static ClassB cb = null;
/**
* 供外界调用获取该类的唯一实例
* static: 方便外界通过类名直接调用该方法
* @return 当前类的唯一实例对象
*/
public static ClassB newClassB(){
//判断是否有可能出现线程安全问题,以此决定是否需要加锁
//二次校验:尽可能减少加锁的时机
if (cb==null) {
//同步代码块: 防止线程安全问题,临界资源对象:当前类的类对象
synchronized (ClassB.class) {
if (cb==null) {//空值判断: 确定是第一次获取实例
cb = new ClassB();
}
}
}
return cb;
}
//构造私有化: 防止外界通过调用构造创建不同对象
private ClassB(){
}
}
懒汉式之懒加载
- 在懒汉式的基础上将内容交于一个静态内部类完成
package com.by.entity;
/**
* 单例-懒汉式-懒加载
*/
public class ClassB2 {
/*
private: 保证外界无法直接访问该属性
static: 1. 保证newClassB2方法可以访问
2. 使属性成为静态属性,内存中只会存在一份,如此就能保证实例化操作只会执行一次
* */
private static ClassB2 cb = null;
//private : 防止外界访问该内部类
private static class Inner{
/**
* 供外界调用获取该类的唯一实例
* static: 方便外界通过类名直接调用该方法
* @return 当前类的唯一实例对象
*/
public static ClassB2 get(){
//判断是否有可能出现线程安全问题,以此决定是否需要加锁
//二次校验:尽可能减少加锁的时机
if (cb==null) {
//同步代码块: 防止线程安全问题,临界资源对象:当前类的类对象
synchronized (ClassB2.class) {
if (cb==null) {//空值判断: 确定是第一次获取实例
cb = new ClassB2();
}
}
}
return cb;
}
}
public static ClassB2 newClassB(){
return Inner.get();
}
//构造私有化: 防止外界通过调用构造创建不同对象
private ClassB2(){
}
}
工厂模式
- 对象的创建和销毁工作交由工厂完成,以此实现在数据操作量较大的情况下分担数据操作的压力提高数据操作的效率,通常用于框架底层
案例演示
目的: 练习配置文件+IO流+Properties集合+反射的使用
案例: 利用工厂获取学生类的实例对象
步骤
-
在com.xxx.entity包下新建Student学生类
package com.by.entity; public class Student { private String name; private int age; private double score; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } public Student() { } public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } }
2. 在当前项目下新建`xxx.properties`配置文件
- 内容: 被工厂管理的类的全限定名
- 书写:
1. 内容以`键=值`的方式存放
2. 键值都不能添加双引号
3. 语句末尾不加分号
4. 语句之中不可存在多余字符
5. 一行只能存在一个键值对
~~~java
StudentClassName=com.by.entity.Student
~~~
3. 新建工厂类.提供方法获取实例对象
~~~java
package com.by.util;
import com.by.entity.Student;
import java.io.FileInputStream;
import java.util.Properties;
/**
* 工厂类
*/
public class MyFactory {
/**
* 获取学生类的实例对象
* @return 学生对象实例
*/
public static Student newStudent(){
Student stu=null;
try (
//创建字节输入流
FileInputStream fis=new FileInputStream("classNames.properties")
) {
//将配置文件中的内容读取存放至Properties集合
Properties p = new Properties();
p.load(fis);
//获取学生类的全限定名
String studentClassName = p.getProperty("StudentClassName");
//通过Class.forName获取类对象
Class c = Class.forName(studentClassName);
//通过类对象构建学生实例
stu = (Student) c.newInstance();
} catch (Exception e) {
System.out.println("未知异常");
e.printStackTrace();
}
return stu;
}
}
- 测试类进行测试
package com.by.test;
import com.by.entity.Student;
import com.by.util.MyFactory;
public class TestMyFactory {
public static void main(String[] args) {
//利用工厂获取学生实例对象
Student s = MyFactory.newStudent();
Student s2 = MyFactory.newStudent();
}
}