JAVA类的加载(3) ——类加载后能够有效运行
例1:
1 package classobject;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.InvocationTargetException;
5 import java.util.Arrays;
6 import java.lang.reflect.Method;
7
8
9
10
11 public class Reflect_Example {
12
13 public static void main(String[] args) {
14 Class<?> lcdTVClass = null;
15 /*
16 疑问:为什么不用new呢 LcdTV lcdTV = new LcdTV(); ? 如果这么写,说明main方法依赖LcdTV这个类,
17 如果这个类不存在,main方法所在的类编译不通过
18
19 原理:main方法执行时候,并不知道我将要加载和使用什么类
20 通常不会在main不可能有明确的new方法去实例化类,因为这个类可能在执行main的时候可能还不存在
21 */
22
23 //注释掉LcdTV类,main方法不报错,说明main方法是没有强依赖于两个业务类的,根据业务类型需要的时候再加载进来
24 try {
25 lcdTVClass = Class.forName("classobject.LcdTV"); //这个类 有可能存在,也有可能不存在;获得这个类的Class对象
26 } catch (ClassNotFoundException e) {
27 e.printStackTrace();
28 }
29
30 System.out.println("类名:"+lcdTVClass.getName());
31 System.out.println(lcdTVClass.getName() + "是一个接口吗?" + lcdTVClass.isInterface());
32
33 Class<?> superClass = lcdTVClass.getSuperclass();
34 System.out.println(lcdTVClass.getName() + "的父类?" + superClass);
35
36 System.out.println(lcdTVClass.getName() + "实现了接口:");
37 Class<?>[] interfaces = lcdTVClass.getInterfaces(); //复数,interfaces
38 for (Class<?> i : interfaces) { // for (Class<?> i : interfaces)
39 System.out.println(" "+i);
40 }
41
42 Object instance = null;
43 try {
44 instance = lcdTVClass.newInstance(); //怎么实例化?(lcdTVClass.newInstance()调用默认构造器实例化)
45 } catch (InstantiationException e) {
46 e.printStackTrace();
47 } catch (IllegalAccessException e) {
48 e.printStackTrace();
49 }
50 System.out.println("使用newInstance()创建对象:" + instance);
51
52 Constructor<?>[] constructors = lcdTVClass.getConstructors(); //通过class对象找到它的构造器
53 for (Constructor<?> con : constructors) {
54 System.out.println(lcdTVClass.getName() + "的构造器" + con);
55 Class<?>[] parameters = con.getParameterTypes();
56 System.out.println("参数类型:" + Arrays.toString(parameters));
57 }
58
59 Object instance2 = null;
60 try {
61 Constructor<?> con = lcdTVClass.getConstructor(int.class); //有参数的构造器如何实例化,传入的是参数类型.class
62 instance2 = con.newInstance(54);
63 System.out.println("使用构造器 " + con + " 创建对象:" + instance2);
64 } catch (SecurityException e) {
65 e.printStackTrace();
66 } catch (NoSuchMethodException e) {
67 e.printStackTrace();
68 } catch (IllegalArgumentException e) {
69 e.printStackTrace();
70 } catch (InstantiationException e) {
71 e.printStackTrace();
72 } catch (IllegalAccessException e) {
73 e.printStackTrace();
74 } catch (InvocationTargetException e) {
75 e.printStackTrace();
76 }
77
78 Method[] methods = lcdTVClass.getMethods(); //获取class对象有多少方法,自己的方法以及从Object继承的方法
79 System.out.println(lcdTVClass.getName() + "的方法");
80 for (Method m : methods) {
81 System.out.println(" " + m);
82 }
83
84 System.out.println("通过反射调用方法turnOn"); //通过反射调用方法
85 try {
86 Method method = lcdTVClass.getMethod("turnOn", new Class[]{}); // new Class[]{}如何使用? turnOn没有参数,空的class数组
87 method.invoke(instance2, new Object[]{}); //new Object[]{} 参数1:实例,参数2:方法的实参列表
88 System.out.println(instance2);
89 } catch (SecurityException e) {
90 e.printStackTrace();
91 } catch (NoSuchMethodException e) {
92 e.printStackTrace();
93 } catch (IllegalArgumentException e) {
94 e.printStackTrace();
95 } catch (IllegalAccessException e) {
96 e.printStackTrace();
97 } catch (InvocationTargetException e) {
98 e.printStackTrace();
99 }
100 }
101 }
102
103 interface Televition {
104 void turnOn();
105 void turnOff();
106 }
107
108 //液晶电视
109 class LcdTV implements Televition {
110
111 private boolean on = false;
112 public final int size; //public final int size=0;会报错
113
114 public LcdTV() {
115 size = 32; //The blank final field size may not have been initialized
116 }
117
118 public LcdTV(int size) {
119 this.size = size;
120 }
121
122 @Override
123 public void turnOn() {
124 System.out.println("电视被打开");
125 on = true;
126 }
127
128 @Override
129 public void turnOff() {
130 System.out.println("电视被关闭");
131 on = false;
132 }
133 }
解释:
1、第18-20行
疑问:为什么不用new呢 LcdTV lcdTV = new LcdTV(); ? 如果这么写,说明main方法依赖LcdTV这个类,如果这个类不存在,main方法所在的类编译不通过 原理:main方法执行时候,并不知道我将要加载和使用什么类 通常不会在main不可能有明确的new方法去实例化类,因为这个类可能在执行main的时候可能还不存在
2、第23行
注释掉LcdTV类,main方法不报错,说明main方法是没有强依赖于两个业务类的,根据业务类型需要的时候再加载进来——利用反射
3、第25行
lcdTVClass = Class.forName("classobject.LcdTV"),获得这个类的Class对象
4、第30-40行
lcdTVClass.getName()获得类名,lcdTVClass.isInterface()判断是否是接口,lcdTVClass.getSuperclass()获得父类,lcdTVClass.getInterfaces()实现了哪些接口
5、怎么实例化
(1)第44行 instance = lcdTVClass.newInstance() 使用Class对象的newInstance()方法来创建该Class对象对应类的实例 (实际上是利用Class对象对应类的默认构造器来创建该类的实例)
(2)第61-63行,有参数的构造器如何实例化,传入的是参数类型.class ——先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()来创建该Class对象对应类的实例
Constructor<?> con = lcdTVClass.getConstructor(int.class); //有参数的构造器如何实例化,传入的是参数类型.class
instance2 = con.newInstance(54);
第(1)种方式是比较常见的,因为很多JAVA EE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序就需要根据该字符串来创建对应的实例,就必须使用反射 ——详见例2
PS:第52行,lcdTVClass.getConstructors() 通过class对象找到它的构造器
6、第78行
Method[] methods = lcdTVClass.getMethods(); //获取class对象有多少方法,自己的方法以及从Object继承的方法
7、第86=87行 通过反射调用方法
Method method = lcdTVClass.getMethod("turnOn", new Class[]{}); // 参数2:方法的参数类型 new Class[]{}如何使用? turnOn没有参数,空的class数组
method.invoke(instance2, new Object[]{}); //new Object[]{} 参数1:实例,参数2:方法的实参列表
执行结果:请手动一行行调试代码
1 类名:classobject.LcdTV
2 classobject.LcdTV是一个接口吗?false
3 classobject.LcdTV的父类?class java.lang.Object
4 classobject.LcdTV实现了接口:
5 interface classobject.Televition
6 使用newInstance()创建对象:classobject.LcdTV@7f5f5897
7 classobject.LcdTV的构造器public classobject.LcdTV()
8 参数类型:[]
9 classobject.LcdTV的构造器public classobject.LcdTV(int)
10 参数类型:[int]
11 使用构造器 public classobject.LcdTV(int) 创建对象:classobject.LcdTV@11cfb549
12 classobject.LcdTV的方法
13 public void classobject.LcdTV.turnOn()
14 public void classobject.LcdTV.turnOff()
15 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
16 public final void java.lang.Object.wait() throws java.lang.InterruptedException
17 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
18 public boolean java.lang.Object.equals(java.lang.Object)
19 public java.lang.String java.lang.Object.toString()
20 public native int java.lang.Object.hashCode()
21 public final native java.lang.Class java.lang.Object.getClass()
22 public final native void java.lang.Object.notify()
23 public final native void java.lang.Object.notifyAll()
24 通过反射调用方法turnOn
25 电视被打开
26 classobject.LcdTV@11cfb549
因此,反射的威力很强大:main在完全不需要感知Lcd电视以及Television这两个类(或接口)时候,可以通过一系列的class的方法来得到它们的信息:有哪些构造器,哪些方法,知道它有多少参数、什么类型;根据相关情况进行调用
反射的优缺点:
1、强大之处:能够在运行时获取类的信息,并进行调用
2、劣势:
(1)没有编译检查做保护:有大量的异常需要处理;重构困难
(2)性能较差
例2:
1 import java.util.*;
2 import java.io.*;
3
4 public class ObjectPoolFactory {
5 //定义一个对象池,前面是对象名,后面是实际对象
6 private Map<String, Object> objectPool = new HashMap<String, Object>();
7
8 //定义一个创建对象的方法,该方法只要传入一个字符串类目,就可以根据该类名生成字符串对象
9 private Object createObject(String clazzName)
10 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
11 //根据字符串获取对应的Class对象
12 Class<?> clazz = Class.forName(clazzName);
13 //使用clazz对应类的默认构造器创建实例
14 return clazz.newInstance();
15 }
16
17 //该方法根据指定文件来初始化对象池,它会根据配置文件来创建对象
18 public void initPool(String fileName)
19 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
20 FileInputStream fis = null;
21 try {
22 fis = new FileInputStream(fileName);
23 Properties props = new Properties();
24 props.load(fis);
25 for (String name : props.stringPropertyNames()) {
26 //每取出一对属性名-属性值对,就根据属性值创建一个对象
27 //调用createObject创建对象,并将对象添加到对象池中
28 objectPool.put(name, createObject(props.getProperty(name)));
29 }
30 } catch(IOException ex) {
31 System.out.println("读取" + fileName + "异常");
32 } finally {
33 try {
34 if (fis != null ) {
35 fis.close();
36 }
37 } catch (IOException ex) {
38 ex.printStackTrace();
39 }
40 }
41 }
42
43 public Object getObject(String name) {
44 //从objectPool中取出指定name对应的对象
45 return objectPool.get(name);
46 }
47
48 public static void main(String[] args)
49 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
50 ObjectPoolFactory pf = new ObjectPoolFactory();
51 pf.initPool("obj.txt");
52 System.out.println(pf.getObject("a"));
53 }
54
55
56 }
结果:
类加载器的补充: 基础可以参考 http://www.cnblogs.com/wxdlut/p/3419462.html
1、Java通过ClassLoader加载Class文件,并生成类对应的Class对象 (类加载 指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象)
2、可以通过Class对象,获取其ClassLoader
例3:
1 package classloader;
2
3 public class Example {
4 public static void main(String[] args) throws ClassNotFoundException {
5 /*
6 通过三种方式获得类的java.lang.Class对象
7 通过.getClassLoader()获取到加载这个类的ClassLoader
8 */
9 ClassLoader cl1 = Example.class.getClassLoader(); //(1)类.class 获取到class对象;
10
11 Example obj = new Example(); //实例化了一个当前这个类Example
12 ClassLoader cl2 = obj.getClass().getClassLoader(); //(2)对象.getClass() 获取到class对象 ;
13
14 //Class.forName("Example")会抛ClassNotFoundException
15 ClassLoader cl3 = Class.forName("classloader.Example").getClassLoader(); //(3)通过Class.forName()活动class对象;
16
17
18 System.out.println("cl1.equals(cl2) ? " + cl1.equals(cl2));
19 System.out.println("cl1.equals(cl3) ? " + cl1.equals(cl3));
20
21 //用cl1这个ClassLoader去加载一个类,但是静态块没有执行;不论用cl1、cl2、cl3 Dog的静态化块都不会被初始化
22 Class<?> loadClass = cl1.loadClass("classloader.Dog");
23 System.out.println(cl1 + ".loadClass加载的类为:" + loadClass.getName());
24
25 // ClassLoader c14 = ClassLoader.getSystemClassLoader(); //获取系统加载器
26
27 }
28 }
29
30 class Dog {
31 static {
32 System.out.println("Dog 的静态块初始化");
33 }
34 }
结果:
cl1.equals(cl2) ? true
cl1.equals(cl3) ? true
sun.misc.Launcher$AppClassLoader@425224ee.loadClass加载的类为:classloader.Dog
解释:
1、第5-19行,如何获得Class对象:
(1)、代码更安全,程序在编译阶段就可以检查要访问的Class对象是否存在 (2)、程序性能更高,无须方法调用
2、第5-19行,通过Class对象,获取其ClassLoader Class对象.getClassLoader()
- java.lang.Object
- java.lang.Class<T>
getClassLoader
public ClassLoader getClassLoader()
Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.If a security manager is present, and the caller's class loader is not null and the caller's class loader is not the same as or an ancestor of the class loader for the class whose class loader is requested, then this method calls the security manager's
checkPermissionmethod with aRuntimePermission("getClassLoader")permission to ensure it's ok to access the class loader for the class.If this object represents a primitive type or void, null is returned.
- Returns:
- the class loader that loaded the class or interface represented by this object.
- Throws:
SecurityException- if a security manager exists and itscheckPermissionmethod denies access to the class loader for the class.- See Also:
ClassLoader,SecurityManager.checkPermission(java.security.Permission),RuntimePermission
3、第25行,获取系统类加载器
ClassLoader c14 = ClassLoader.getSystemClassLoader(); //获取系统加载器
Class ClassLoader
- java.lang.Object
- java.lang.ClassLoader
getSystemClassLoader
public static ClassLoader getSystemClassLoader()
Returns the system class loader for delegation. This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application.This method is first invoked early in the runtime's startup sequence, at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.
The default system class loader is an implementation-dependent instance of this class.
If the system property "java.system.class.loader" is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader. The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent. An instance is then created using this constructor with the default system class loader as the parameter. The resulting class loader is defined to be the system class loader.
If a security manager is present, and the invoker's class loader is not null and the invoker's class loader is not the same as or an ancestor of the system class loader, then this method invokes the security manager's
checkPermissionmethod with aRuntimePermission("getClassLoader")permission to verify access to the system class loader. If not, a SecurityException will be thrown.- Returns:
- The system ClassLoader for delegation, or null if none
- Throws:
SecurityException- If a security manager exists and its checkPermission method doesn't allow access to the system class loader.IllegalStateException- If invoked recursively during the construction of the class loader specified by the "java.system.class.loader" property.Error- If the system property "java.system.class.loader" is defined but the named class could not be loaded, the provider class does not define the required constructor, or an exception is thrown by that constructor when it is invoked. The underlying cause of the error can be retrieved via theThrowable.getCause()method.
JAVA类的加载(3) ——类加载后能够有效运行的更多相关文章
- jvm系列(一):java类的加载机制
java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...
- 【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...
- JAVA类的加载、连接与初始化
JAVA类的加载.连接与初始化 类的声明周期总共分为5个步骤1.加载2.连接3.初始化4.使用5.卸载 当java程序需要某个类的时候,java虚拟机会确保这个类已经被加载.连接和初始化,而连接这个类 ...
- java 类的加载、连接和初始化
JVM和类 调用Java命令运行Java程序时,该命令将会启动一条Java虚拟机进程,不管该Java程序启动了多少条线程,创建了多少个变量,它们都处于该Java虚拟机进程里,共享该JVM进程的内存区. ...
- 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...
- java类从加载、连接到初始化过程
类加载器 在了解Java的机制之前,需要先了解类在JVM(Java虚拟机)中是如何加载的,这对后面理解java其它机制将有重要作用. 每个类编译后产生一个Class对象,存储在.class文件中,JV ...
- JVM(1):Java 类的加载机制
原文出处: 纯洁的微笑 java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang. ...
- 透过现象看本质:Java类动态加载和热替换
摘要:本文主要介绍类加载器.自定义类加载器及类的加载和卸载等内容,并举例介绍了Java类的热替换. 最近,遇到了两个和Java类的加载和卸载相关的问题: 1) 是一道关于Java的判断题:一个类被首次 ...
- 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)
Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...
- JAVA - 类的加载过程
JAVA - 类的加载过程 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象 ...
随机推荐
- ARM Trusted Firmware——编译选项(二)
@ 目录 1. 常用部分 2. 安全相关 2.1 签名 2.2 加密 2.3 哈希 2.4 中断 3.GICv3驱动程序选项 4. 调试选项 1. 常用部分 编译选项 解释 BL2 指定生成fip文件 ...
- MySQL到ClickHouse数据同步方案
MySQL 同步到 ClickHouse的方案可以看下面的说明,选择合适最近的同步方法. 1. 对比结果概述 整体上,NineData(官网:www.ninedata.cloud )的数据复制功能在功 ...
- Spring-Bean的依赖注入的数据类型
Spring-Bean的依赖注入的数据类型 除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入 数据的三种数据类型 普通数据类型 引用数据类型 集合数据类型 普通数据类型 public ...
- nuitka 将程序编译为单个exe
原文链接:https://github.com/Nuitka/Nuitka 在Windows上,有两种模式,一种是将其复制到您公司指定的AppData,也将其用作缓存,另一种是在临时目录中进行.您需要 ...
- [grpc]双向tls加密认证
前言 假设gRPC服务端的主机名为qw.er.com,需要为gRPC服务端和客户端之间的通信配置tls双向认证加密. 生成证书 生成ca根证书.生成过程会要求填写密码.CN.ON.OU等信息,记住密码 ...
- 微信的 h5 支付和 jsapi 支付
目录 申请商户号 申请商户证书 设置APIv3密钥 下载 SDK 开发包 下载平台证书 关联 AppID 账号 开通 H5 支付 H5支付流程 开通 JSAPI 支付 JSAPI 支付流程 通用微信支 ...
- 使用API接口获取商品数据
在当今的数字化时代,商品数据的获取对于各种规模的企业来说都至关重要.这些数据可以帮助企业进行市场分析,制定销售策略,优化库存管理,以及实现精准营销.API(应用程序编程接口)是一种便捷的方式来获取 ...
- WPF 自定义窗体(一)
.Net默认的窗体样式只有四种:None.SingleBorderWindow.ThreeDBorderWindow.ToolWindow,都比较"丑".而很多时候,我们希望自定义 ...
- Ipa打包并安装到iphone
手动运行篇: 在真机上运行appium会进行闪退,因为我们的真机是不合法的真机,怎么样才能合法呢,要注册我们的设备才行 要对app进行打包,要先进行签名,要签名,就需要证书,证书可以自己伪造,但是这一 ...
- Hugging News #0904:🤗 登陆 AWS Marketplace
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...