java反射(二)--反射应用案例
一.反射实例化对象
经过一系列的分析之后发现虽然可以获取Class类的实例化对象,但是依然觉得这个对象的获取意义不是很大,因此可以通过以下几个案例去理解反射的核心意义
--反射实例化对象:获取Class对象之后最大的意义并不是在于只是一个对象的实例化操作形式,更重要的是Class类中提供有一个对象的反射实例化方法,在JDK1.9之前的实例化:public T newInstance() throw InstantiationException,IllegalAccessException,该方法代替了new 关键字的使用,但是在JDK1.9之后则发生了变化:class.getDeclaredConstructor().newInstance();
--范例:通过newInstance()方法实例化对象
package 反射.认识反射机制.entity; /**
* @author : S K Y
* @version :0.0.1
*/
public class Person {
public Person() { //任何情况下只要实例化对象则一定要调用类中的构造方法
System.out.println("Person对象实例化了");
} @Override
public String toString() {
return "我是一个好人";
}
}
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> aClass = Class.forName("反射.认识反射机制.entity.Person");
Object o = aClass.newInstance(); //实例化对象
System.out.println(o);
}
}
--运行结果
Person对象实例化了
我是一个好人 Process finished with exit code 0
--现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于new 关键字的使用,但是该方法在JDK1.9之后被替代了,因为默认的Class类中的newInstance()方法只能够调用无参构造,所以很多的开发者认为其描述的不准确,于是将其变换了形式(后续会说明)
二.反射与工厂设计模式
如果要想进行对象的实例化处理除了可以使用关键字new 之外,还可以挺过反射机制来完成.那么思考一个问题:为什么要提供有一个反射的实例化?到底是使用关键字new还是使用反射进行对象实例化呢?
--如果想要更好的解决此类问题,最好的解释方案就是通过工厂设计模式来解决.工厂设计模式的最大特点:客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂了获取接口的实例化对象,传统的工厂设计模式:
interface Message{
public void send(); //消息发送
}
class NetMessage implements Message{ //网络消息实现类
@Override
public void send() {
System.out.println("发送网络消息");
}
}
public class FactoryDemo {
public static void main(String[] args) {
Message message = new NetMessage(); //如果直接实例化则一定会有耦合问题
}
}
--在实际的开发中,接口的主要作用是为不同的层提供有一个操作的标准.但是此时如果直接将一个子类设置为接口实例化操作,那么一定会有耦合问题,所以使用了工厂设计模式来解决此问题.
--范例:传统的工厂设计模式
interface Message {
public void send(); //消息发送
} class NetMessage implements Message { //网络消息实现类
@Override
public void send() {
System.out.println("发送网络消息");
}
} class Factory {
private Factory() {
} //没有产生实例化对象的意义 public static Message getInstance(String className) {
if ("NetMessage".equals(className)) {
return new NetMessage();
}
return null;
}
} public class FactoryDemo {
public static void main(String[] args) {
Message message = Factory.getInstance("NetMessage");
message.send();
}
}
--此种工厂设计模式属于静态工厂设计模式,此时如果追加一个子类,那么工厂类就需要进行相应的修改(追加相应的判断语句),否则无法获得新的子类的实例化对象.工厂设模式最有效解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供有一个工厂类作为过渡端,可是随着项目的进行,Message接口可能会有更多的子类,而且随着时间的推移,子类会越来越多,因此工厂类永远都需要修改,并且永无停止之日.
--此时最好的解决方案就是不使用关键字new来完成对象的实例化,因为关键字new在使用的时候需要有一个明确的类存在.而newInstance()的方法只需要有一个明确表示类名称的字符串即可应用:
interface Message {
public void send(); //消息发送
} class NetMessage implements Message { //网络消息实现类
@Override
public void send() {
System.out.println("发送网络消息");
}
} class Factory {
private Factory() {
} //没有产生实例化对象的意义 public static Message getInstance(String className) throws Exception {
return (Message) Class.forName(className).newInstance();
}
} public class FactoryDemo {
public static void main(String[] args) throws Exception {
Message message = Factory.getInstance("反射.反射应用案例.NetMessage");
message.send();
}
}
--此时如果对子类继续进行扩充的话,是没有必要修改工厂类的.利用反射机制实现的工厂设计模式,最大的优势在于,对于接口的子类的扩充,将不再影响到工厂类的定义.但是现在依然需要进行思考,在实际的项目开发之中,有可能会存在大量的接口,并且这些接口可能都需要通过工厂类来实例化对象,所以此时的工厂设计模式不应该只为一个Message接口服务,而应该变为为所有的接口服务(使用泛型实现开发需求):
interface Service {
public void service();
} class HouseService implements Service {
@Override
public void service() {
System.out.println("为您的住房提供服务.");
}
} interface Message {
public void send(); //消息发送
} class NetMessage implements Message { //网络消息实现类
@Override
public void send() {
System.out.println("发送网络消息");
}
} class Factory {
private Factory() {
} //没有产生实例化对象的意义 /**
* 获取接口实例化对象
*
* @param className 接口的子类
* @param tClass 描述的是一个接口的类型
* @return 如果子类存在则返回指定接口
* @throws Exception
*/
public static <T> T getInstance(String className, Class<T> tClass) throws Exception {
return tClass.cast(Class.forName(className).newInstance());
}
} public class FactoryDemo {
public static void main(String[] args) throws Exception {
Message message = Factory.getInstance("反射.反射应用案例.NetMessage",Message.class);
message.send();
Service instance = Factory.getInstance("反射.反射应用案例.HouseService", Service.class);
instance.service();
}
}
--此时的工厂设计模式才是所谓的高可用的工厂设计模式,而这种操作的实现依赖的就是泛型.此时的工厂设计模式将不再受限于指定的接口,可以为所有的接口提供实例化对象.
三.反射与单例设计模式
单例设计模式的核心本质在于类内部的构造方法私有化,在类的内部产生实例化对象之后在外部通过static方法获取到实例化对象进行类中的结构调用.单例设计模式一共有两种,懒汉式和饿汉式(饿汉式的单例是不再本次的讨论范围之内的,主要讨论懒汉式的单例)
--范例:观察懒汉式单例的问题
class Singleton {
private static Singleton instance = null; private Singleton() {
} public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
} public void print() {
System.out.println("单例模式加载");
} } public class LazyLoadDemo {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.print();
}
}
--此时我们的操作是在单线程的环境下运行的,如果使用多线程
class Singleton {
private static Singleton instance = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
} public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
} public void print() {
System.out.println("单例模式加载");
} } public class LazyLoadDemo {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
Singleton.getInstance();
}, "[单例创建者" + (i + 1) + "]").start();
}
}
}
--运行结果
[单例创建者1] 实例化单例对象
[单例创建者2] 实例化单例对象
[单例创建者3] 实例化单例对象 Process finished with exit code 0
--单例设计模式最大的特点是在整体运行之中,只允许产生一个实例化对象,当有了若干实例化对象之后,那么就不是单例设计模式了,我们可以大致分析单例模式的运行流程如下:
1.判断instance是否为空?
2.如果instance为空,实例化instance对象
3.返回当前的instance
--因此在多线程的设计中,每一个线程在执行步骤1的时候都会认为此时的对象为空,那么都会去创建这个对象的实例,这样一来单例设计模式也就失去了意义,如果想要解决这类问题,关键的核心就在于要解决同步处理,而解决同步处理的核心就是使用synchronized关键字
class Singleton {
private static Singleton instance = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
} public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
} public void print() {
System.out.println("单例模式加载");
} } public class LazyLoadDemo {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
Singleton.getInstance();
}, "[单例创建者" + (i + 1) + "]").start();
}
}
}
--运行结果
[单例创建者1] 实例化单例对象 Process finished with exit code 0
--此时却是进行了同步处理,但是这个同步的代价却是很大的,因为效率会降低.因为整体代码中实际上只有一块区域需要同步处理,那就是instance对象的实例化处理部分,在这样的情况下同步加的未免显得有些草率,更加合理的进行同步处理:
class Singleton {
//在对象实例化的时候,应该立刻与主内存中的实例对象保持同步,而不应该存在副本
private static volatile Singleton instance = null; private Singleton() {
System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
} public static Singleton getInstance() {
synchronized (Singleton.class) { //static方法只能使用Singleton.class
if (instance == null) {
instance = new Singleton();
}
}
return instance;
} public void print() {
System.out.println("单例模式加载");
} } public class LazyLoadDemo {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
Singleton.getInstance();
}, "[单例创建者" + (i + 1) + "]").start();
}
}
}
java反射(二)--反射应用案例的更多相关文章
- java中的反射(二)
java中的反射(一):https://www.cnblogs.com/KeleLLXin/p/14060555.html 目录 一.反射 1.class类 2.访问字段 3.调用方法 4.调用构造方 ...
- 浅说Java中的反射机制(二)
写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...
- 重学JAVA基础(二):Java反射
看一下百度的解释: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息 ...
- 【Java基础】java中的反射机制与动态代理
一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...
- Java学习之反射篇
Java学习之反射篇 0x00 前言 今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要. 0x01 反射机制概述 Java反射是Java非 ...
- java反射 之 反射基础
一.反射 反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 ...
- 【转】Java反射 之 反射基础
一.反射 反射:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 ...
- Java进阶之reflection(反射机制)——反射概念与基础
反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...
- java 27 - 2 反射之 反射的概述以及获取Class文件对象的方式
反射: JAVA语言的反射机制: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调 ...
随机推荐
- SQL 日期格式化与格式转化
日期格式化 Select CONVERT(varchar(), GETDATE(), ): :57AM Select CONVERT(varchar(), GETDATE(), ): // Selec ...
- Panabit镜像功能配合wireshark抓包的方法
Panabit镜像功能配合wireshark抓包的方法 Panabit的协议识别都是基于数据包的特征,因此捕获数据包样本是我们进行识别第一步要做的事情.下面就和大家说一下如何捕获网络应用的数据包. 到 ...
- 搭建阿里云服务器(centos,jdk和Tomcat版本)
1.购买服务器(登录阿里云,购买服务器,并进入控制台,查看自己的服务器实例 2.域名注册(这步可以省略,直接IP地址访问,因为域名需要备案),购买域名的需要进行解析以及绑定自己的服务器 3.可以准备一 ...
- Redis这篇就够了
Redis 简介 Redis 优势 Redis 数据类型 string hash list set Zset 小总结 基本命令 发布订阅 简介 实例 发布订阅常用命令 事务 实例 Redis 事务命令 ...
- shell脚本实现ftp上传下载文件
前段时间工作中需要将经过我司平台某些信息核验数据提取后上传到客户的FTP服务器上,以便于他们进行相关的信息比对核验.由于包含这些信息的主机只有4台,采取的策略是将生成的4个文件汇集到一个主机上,然后在 ...
- Vue:子组件如何跟父组件通信
我们知道,父组件使用 prop 传递数据给子组件.但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了. 使用 v-on 绑定自定义事件 每个 Vue 实例都实现了事件接口,即 ...
- [BZOJ4278] [ONTAK2015]Tasowanie 贪心+后缀数组
题目链接 最近做题目好像有点东一榔头西一棒.好吧其实订正模拟题的时候需要用到什么感觉不太熟的就写一下吧. 显然直接贪心,比较两个点后面的串的字典序,小就选谁就可以了. 可以把两个串接起来,加一个\(i ...
- Stream学习笔记
1. 创建Stream实例的五种方式 @Test public void test1(){ // 创建Stream对象的第一种方式 List<String> list = Lists.ne ...
- orcale获取表、字段信息
获取表字段: select * from user_tab_columns where Table_Name='用户表' order by column_name 获取表注释: select * fr ...
- sqlserver 中的时间算法
DECLARE @Date DATETIME SET @Date=GETDATE() --前一天,给定日期的前一天 ,@Date) AS '前一天' --后一天,给定日期的后一天 ,@Date) AS ...