この章では、java.lang.reflect パッケージのインターフェースとクラスに焦点を当てます
プログラムが特定のクラスを使用するとき、そのクラスがメモリにロードされていない場合、システムはロードします。クラスを初期化するには 3 つの手順があります。クラスをロードするときは、クラス ファイルをメモリに読み込み、そのクラス用の java.lang.class オブジェクトを作成します。つまり、プログラムでクラスが使用されるときです。 、システムはそのための java.lang.Class オブジェクトを作成します (ほとんどすべてのクラスは
)。 java.long.Object class.
Java では、クラスは完全修飾クラス名 (パッケージ名とクラス名を含む) によって識別されますが、JVM では、クラスは完全修飾クラス名とそのクラス ローダーによって識別されます。一意の識別子。ルート クラス ローダーを印刷します:
public class BootstrapTest { public static void main(String[] args) { // 获取根类加载器所加载的全部URL数组 for (URL url : sun.misc.Launcher.getBootstrapClassPath().getURLs()) { // 遍历、输出根类加载器加载的全部URL System.out.println(url.toExternalForm()); } } } ---------------------------------------------------------------------- file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/resources.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/rt.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jsse.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jce.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/charsets.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jfr.jar拡張クラス ローダー。独自の jar パッケージを追加できます。これはとても楽しいです。
システム クラス ローダー: これは通常使用するものです。クラスの親ローダー
開発者は、ClassLoaderを継承してカスタムクラスローダーを実装する必要があります。
public static void main(String[] args) throws IOException { // 获取系统类加载器 ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); System.out.println("系统类加载器:" + systemLoader); /* 获取系统类加载器的加载路径——通常由CLASSPATH环境变量指定 如果操作系统没有指定CLASSPATH环境变量,默认以当前路径作为 系统类加载器的加载路径 */ Enumeration<URL> em1 = systemLoader.getResources(""); while(em1.hasMoreElements()) { System.out.println(em1.nextElement()); } // 获取系统类加载器的父类加载器:得到扩展类加载器 ClassLoader extensionLader = systemLoader.getParent(); System.out.println("扩展类加载器:" + extensionLader); System.out.println("扩展类加载器的加载路径:" + System.getProperty("java.ext.dirs")); System.out.println("扩展类加载器的parent: " + extensionLader.getParent()); }
URLClassLoad以降Java8のえー.0.51 では、ClassLoader クラスの次のメソッドが書き換えられています
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }そこで、findClass(String name) を自分で書き換えると、プログラムは最初に URLClassLoader の findClass メソッドを呼び出すことになります。このメソッドがクラスを見つけられない場合は、自分で作成した findClass(String name) を呼び出します たとえば、特定のクラスを動的にロードする場合 (このクラスがメモリに存在する場合は、 ;memory ;メモリに存在しない場合は、そのクラスのJavaファイルをロードしてコンパイルします(ソースコードと自分のテスト結果を見て、最初にクラスファイルがあるかどうかを確認するかどうかはわかりませんが、はチェックされていません)).例
public class Hello { public static void main(String[] args) { System.out.println("tes22t2"); for (String arg : args) { System.out.println("运行Hello的参数:" + arg); } } }
import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.security.CodeSigner; import java.security.CodeSource; import java.util.jar.Manifest; import sun.misc.Resource; import sun.misc.URLClassPath; import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf; /** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public class CompileClassLoader extends ClassLoader { // 读取一个文件的内容 private byte[] getBytes(String filename) throws IOException { File file = new File(filename); long len = file.length(); byte[] raw = new byte[(int)len]; try( FileInputStream fin = new FileInputStream(file)) { // 一次读取class文件的全部二进制数据 int r = fin.read(raw); if(r != len) throw new IOException("无法读取全部文件:" + r + " != " + len); return raw; } } // 定义编译指定Java文件的方法 private boolean compile(String javaFile) throws IOException { System.out.println("CompileClassLoader:正在编译 " + javaFile + "..."); // 调用系统的javac命令 Process p = Runtime.getRuntime().exec("javac " + javaFile); try { // 其他线程都等待这个线程完成 p.waitFor(); } catch(InterruptedException ie) { System.out.println(ie); } // 获取javac线程的退出值 int ret = p.exitValue(); // 返回编译是否成功 return ret == 0; } // 重写ClassLoader的findClass方法 @Override protected Class<?> findClass(String tmpName) throws ClassNotFoundException { System.out.println(tmpName); Class clazz = null; // 将包路径中的点(.)替换成斜线(/); String className = tmpName.replace("." , "/").replace("1" , ""); // 这里因为我是在eclipse里编辑的,而java文件统一放到src里,class文件统一放到了bin里, 所以我需要加前缀, String javaFilename = "src/"+className + ".java"; String classFilename = "bin/"+className + ".class"; File javaFile = new File(javaFilename); System.out.println(javaFile.getAbsolutePath()); File classFile = new File(classFilename); // 当指定Java源文件存在,且class文件不存在、或者Java源文件 // 的修改时间比class文件修改时间更晚,重新编译 if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified() > classFile.lastModified())) { try { // 如果编译失败,或者该Class文件不存在 if(!compile(javaFilename) || !classFile.exists()) { throw new ClassNotFoundException( "ClassNotFoundExcetpion:" + javaFilename); } } catch (IOException ex) { ex.printStackTrace(); } } // 如果class文件存在,系统负责将该文件转换成Class对象 if (classFile.exists()) { try { // 将class文件的二进制数据读入数组 byte[] raw = getBytes(classFilename); // 调用ClassLoader的defineClass方法将二进制数据转换成Class对象 clazz = defineClass(className,raw,0,raw.length); } catch(IOException ie) { ie.printStackTrace(); } } // 如果clazz为null,表明加载失败,则抛出异常 if(clazz == null) { throw new ClassNotFoundException(className); } return clazz; } // 定义一个主方法 public static void main(String[] args) throws Exception { // 如果运行该程序时没有参数,即没有目标类 args = new String[]{"Hello","java疯狂讲义w"}; // 第一个参数是需要运行的类 String progClass = args[0]; // 剩下的参数将作为运行目标类时的参数, // 将这些参数复制到一个新数组中 String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 加载需要运行的类 Class<?> clazz = ccl.loadClass(progClass); // 获取需要运行的类的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } }出力 結果は
tes22t2 运行Hello的参数:java疯狂讲义wブレークポイントが表示され、カスタム findClass(String tmpName) メソッドは呼び出されません
当我们把 args = new String[]{"Hello","java疯狂讲义w"}; 改为 args = new String[]{"Hello1","java疯狂讲义w"}; 时,由于URLClassLoader并没有找到被加载的类Hello1,所以最后会调用我们自定义的findClass方法. 当然,更规范的是把 Class<?> clazz = ccl.loadClass(progClass); 改为 Class<?> clazz = ccl.findClass(progClass);出力結果:
Hello1/Users/liuxin/work/workspace2/learnJava/src/Hello.java tes22t2 运行Hello的参数:java疯狂讲义wここで次のようになります。クラスを動的にロードしたい場合は、自分で上書きする必要がないことを確認してください。 findClass メソッドを記述するには、次のコードだけが必要です:
import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.security.CodeSigner; import java.security.CodeSource; import java.util.jar.Manifest; import sun.misc.Resource; import sun.misc.URLClassPath; import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf; public class CompileClassLoader extends ClassLoader { public static void main(String[] args) throws Exception { // 如果运行该程序时没有参数,即没有目标类 args = new String[]{"Hello","java疯狂讲义w"}; // 第一个参数是需要运行的类 String progClass = args[0]; // 剩下的参数将作为运行目标类时的参数, // 将这些参数复制到一个新数组中 String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 加载需要运行的类 Class<?> clazz = ccl.loadClass(progClass); // 获取需要运行的类的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } } ----------------输出结果-------------------- tes22t2 运行Hello的参数:java疯狂讲义w18.2.4 URLClassLoader クラスjava は、ClassLoader の URLClassLoader 実装クラスを提供します。これは、システム クラス ローダーと拡張クラス ローダーの親クラスでもあります (ここでのクラスとは、クラス間の継承関係を指します)。URLClassLoader は強力で、ローカルまたはリモート ホストからバイナリ ファイルを取得してクラスをロードできます。は例です
import java.sql.*; import java.util.*; import java.net.*; /** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public class URLClassLoaderTest { private static Connection conn; // 定义一个获取数据库连接方法 public static Connection getConn(String url , String user , String pass) throws Exception { if (conn == null) { // 创建一个URL数组 URL[] urls = {new URL( "file:mysql-connector-java-5.1.30-bin.jar")}; // 以默认的ClassLoader作为父ClassLoader,创建URLClassLoader URLClassLoader myClassLoader = new URLClassLoader(urls); // 加载MySQL的JDBC驱动,并创建默认实例 Driver driver = (Driver)myClassLoader. loadClass("com.mysql.jdbc.Driver").newInstance(); // 创建一个设置JDBC连接属性的Properties对象 Properties props = new Properties(); // 至少需要为该对象传入user和password两个属性 props.setProperty("user" , user); props.setProperty("password" , pass); // 调用Driver对象的connect方法来取得数据库连接 conn = driver.connect(url , props); } return conn; } public static void main(String[] args)throws Exception { Connection temconn = getConn("jdbc:mysql://localhost:3306/mybatis" , "root" , "password"); try{ String sql = "INSERT INTO `mybatis`.`users` (`NAME`, `age`) VALUES ('java8', '2')"; java.sql.PreparedStatement stmt = temconn.prepareStatement(sql); stmt.execute(); stmt.close(); String sql2 = "select * from `mybatis`.`users`"; java.sql.PreparedStatement stmt2 = temconn.prepareStatement(sql2); ResultSet rs = stmt2.executeQuery(); ResultSetMetaData m=rs.getMetaData(); //显示列,表格的表头 int columns=m.getColumnCount(); for(int i=1;i<=columns;i++) { System.out.print(m.getColumnName(i)); System.out.print("\t\t"); } System.out.println(); //显示表格内容 while(rs.next()) { for(int i=1;i<=columns;i++) { System.out.print(rs.getString(i)); System.out.print("\t\t"); } System.out.println(); } stmt2.close(); }catch(Exception e){ e.printStackTrace(); }finally{ temconn.close(); } } }したがって、以前の動的にロードされたクラスは URLClassLoader で使用できます。 次のように書き換えられます: (パスを理解するために、Hello.java が learnJava パッケージに入れられました)
public static void main(String[] args) throws Exception { args = new String[]{"learnJava.Hello","java疯狂讲义w"}; String progClass = args[0]; String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs, 0 , progArgs.length); URL[] urls = {new URL("file:")}; Class<?> clazz = (new URLClassLoader(urls)).loadClass(progClass); // 获取需要运行的类的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); }
18.3 を通してクラス情報を表示します。リフレクション
いつリフレクションが使用されますか?
クラス内の情報を取得するには、メソッドは次のカテゴリに分類されます:
1. コンストラクターを取得する
2. メソッドを取得する
属性 上記の 3 つのメソッドのカテゴリは、各カテゴリが次の 4 つのメソッドに分かれています: (単一、複数) * (許可に関係なく、許可による)4. アノテーションを取得します。これは多すぎます。
5. 内部クラスを取得します
Class>[] getDeclaredClasses():返回该Class对象对应类包含的全部内部类
6.获取外部类
Class>[] getDeclaringClasse():返回该Class对象对应类所在的外部类
7.获取接口
Class>[] getInterfaces():返回该Class对象对应类所实现的全部接口
8.其他的如下:
例子:
// 定义可重复注解 @Repeatable(Annos.class) @interface Anno {} @Retention(value=RetentionPolicy.RUNTIME) @interface Annos { Anno[] value(); } // 使用4个注解修饰该类 @SuppressWarnings(value="unchecked") @Deprecated // 使用重复注解修饰该类 @Anno @Anno public class ClassTest { // 为该类定义一个私有的构造器 private ClassTest() { } // 定义一个有参数的构造器 public ClassTest(String name) { System.out.println("执行有参数的构造器"); } // 定义一个无参数的info方法 public void info() { System.out.println("执行无参数的info方法"); } // 定义一个有参数的info方法 public void info(String str) { System.out.println("执行有参数的info方法" + ",其str参数值:" + str); } // 定义一个测试用的内部类 class Inner { } public static void main(String[] args) throws Exception { // 下面代码可以获取ClassTest对应的Class Class<?> clazz = ClassTest.class; // 获取该Class对象所对应类的全部构造器 Constructor[] ctors = clazz.getDeclaredConstructors(); System.out.println("ClassTest的全部构造器如下:"); for (Constructor c : ctors) { System.out.println(c); } // 获取该Class对象所对应类的全部public构造器 Constructor[] publicCtors = clazz.getConstructors(); System.out.println("ClassTest的全部public构造器如下:"); for (Constructor c : publicCtors) { System.out.println(c); } // 获取该Class对象所对应类的全部public方法 Method[] mtds = clazz.getMethods(); System.out.println("ClassTest的全部public方法如下:"); for (Method md : mtds) { System.out.println(md); } // 获取该Class对象所对应类的指定方法 System.out.println("ClassTest里带一个字符串参数的info()方法为:" + clazz.getMethod("info" , String.class)); // 获取该Class对象所对应类的上的全部注解 Annotation[] anns = clazz.getAnnotations(); System.out.println("ClassTest的全部Annotation如下:"); for (Annotation an : anns) { System.out.println(an); } System.out.println("该Class元素上的@SuppressWarnings注解为:" + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class))); System.out.println("该Class元素上的@Anno注解为:" + Arrays.toString(clazz.getAnnotationsByType(Anno.class))); // 获取该Class对象所对应类的全部内部类 Class<?>[] inners = clazz.getDeclaredClasses(); System.out.println("ClassTest的全部内部类如下:"); for (Class c : inners) { System.out.println(c); } // 使用Class.forName方法加载ClassTest的Inner内部类 Class inClazz = Class.forName("ClassTest$Inner"); // 通过getDeclaringClass()访问该类所在的外部类 System.out.println("inClazz对应类的外部类为:" + inClazz.getDeclaringClass()); System.out.println("ClassTest的包为:" + clazz.getPackage()); System.out.println("ClassTest的父类为:" + clazz.getSuperclass()); } }
18.3.3 java8 新增的方法参数反射
关于反射方法的参数的名字,这个比较麻烦,如下的例子
class Test { public void replace(String str, List<String> list){} } public class MethodParameterTest { public static void main(String[] args)throws Exception { // 获取Tesy的类 Class<Test> clazz = Test.class; // 获取Test类的带两个参数的replace()方法 Method replace = clazz.getMethod("replace" , String.class, List.class); // 获取指定方法的参数个数 System.out.println("replace方法参数个数:" + replace.getParameterCount()); // 获取replace的所有参数信息 Parameter[] parameters = replace.getParameters(); System.out.println((new File("")).getAbsolutePath()); int index = 1; // 遍历所有参数 for (Parameter p : parameters) { if (p.isNamePresent()) { System.out.println("---第" + index++ + "个参数信息---"); System.out.println("参数名:" + p.getName()); System.out.println("形参类型:" + p.getType()); System.out.println("泛型类型:" + p.getParameterizedType()); } } } }
所以,上面这个例子在用eclipse编译时,总是找不到参数名.没找到设置的地方,只能用javac编译
编译命令如下: javac -parameters -encoding GBK -d . MethodParameterTest.java 因为疯狂java讲义里的源码都是GBK编码,而一般的电脑默认utf-8,所以需要指定编码方式 运行: java MethodParameterTest ----------------------结果-------------------------- replace方法参数个数:2 /Users/liuxin/work/workspace2/learnJava/src ---第1个参数信息--- 参数名:str 形参类型:class java.lang.String 泛型类型:class java.lang.String ---第2个参数信息--- 参数名:list 形参类型:interface java.util.List 泛型类型:java.util.List<java.lang.String>
18.4 使用反射生成并操作对象
例子:这个个别地方没理解,但是这是一个反射的典型应用,需要好好理解
import java.util.*; import java.io.*; import java.lang.reflect.*; /** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public class ExtendedObjectPoolFactory { // 定义一个对象池,前面是对象名,后面是实际对象 private Map<String ,Object> objectPool = new HashMap<>(); private Properties config = new Properties(); // 从指定属性文件中初始化Properties对象 public void init(String fileName) { // File tmpFi = new File(fileName); // System.out.println(tmpFi.getAbsolutePath()); try(FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { ex.printStackTrace(); System.out.println("读取" + fileName + "异常"); } } // 定义一个创建对象的方法, // 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象 private Object createObject(String clazzName) throws InstantiationException , IllegalAccessException , ClassNotFoundException { // 根据字符串来获取对应的Class对象 Class<?> clazz =Class.forName(clazzName); // 使用clazz对应类的默认构造器创建实例 return clazz.newInstance(); } // 该方法根据指定文件来初始化对象池, // 它会根据配置文件来创建对象 public void initPool()throws InstantiationException ,IllegalAccessException , ClassNotFoundException { for (String name : config.stringPropertyNames()) { // 每取出一对key-value对,如果key中不包含百分号(%) // 这就标明是根据value来创建一个对象 // 调用createObject创建对象,并将对象添加到对象池中 if (!name.contains("%")) { objectPool.put(name , createObject(config.getProperty(name))); } } } // 该方法将会根据属性文件来调用指定对象的setter方法 public void initProperty()throws InvocationTargetException ,IllegalAccessException,NoSuchMethodException { for (String name : config.stringPropertyNames()) { // 每取出一对key-value对,如果key中包含百分号(%) // 即可认为该key用于控制调用对象的setter方法设置值, // %前半为对象名字,后半控制setter方法名 if (name.contains("%")) { // 将配置文件中key按%分割 String[] objAndProp = name.split("%"); // 取出调用setter方法的参数值 Object target = getObject(objAndProp[0]); // 获取setter方法名:set + "首字母大写" + 剩下部分 String mtdName = "set" + objAndProp[1].substring(0 , 1).toUpperCase() + objAndProp[1].substring(1); // 通过target的getClass()获取它实现类所对应的Class对象 Class<?> targetClass = target.getClass(); // 获取希望调用的setter方法 Method mtd = targetClass.getMethod(mtdName , String.class); // 通过Method的invoke方法执行setter方法, // 将config.getProperty(name)的值作为调用setter的方法的参数 mtd.invoke(target , config.getProperty(name)); } } } public Object getObject(String name) { // 从objectPool中取出指定name对应的对象。 return objectPool.get(name); } public static void main(String[] args) throws Exception { ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory(); epf.init("src/extObj.txt"); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject("a")); } }
例子二:使用指定的构造器来构造对象.
public class CreateJFrame { public static void main(String[] args) throws Exception { // 获取JFrame对应的Class对象 Class<?> jframeClazz = Class.forName("javax.swing.JFrame"); // 获取JFrame中带一个字符串参数的构造器 Constructor ctor = jframeClazz .getConstructor(String.class); // 调用Constructor的newInstance方法创建对象 Object obj = ctor.newInstance("测试窗口"); // 输出JFrame对象 System.out.println(obj); } }
18.4.3 访问成员变量值
例子:
class Person { private String name; private int age; public String toString() { return "Person[name:" + name + " , age:" + age + " ]"; } } public class FieldTest { public static void main(String[] args) throws Exception { // 创建一个Person对象 Person p = new Person(); // 获取Person类对应的Class对象 Class<Person> personClazz = Person.class; // 获取Person的名为name的成员变量 // 使用getDeclaredField()方法表明可获取各种访问控制符的成员变量 Field nameField = personClazz.getDeclaredField("name"); // 设置通过反射访问该成员变量时取消访问权限检查 nameField.setAccessible(true); // 调用set()方法为p对象的name成员变量设置值 nameField.set(p , "Yeeku.H.Lee"); // 获取Person类名为age的成员变量 Field ageField = personClazz.getDeclaredField("age"); // 设置通过反射访问该成员变量时取消访问权限检查 ageField.setAccessible(true); // 调用setInt()方法为p对象的age成员变量设置值 ageField.setInt(p , 30); System.out.println(p); } }
用java.lang.reflect包下的Array类操作数组
例子:
public class ArrayTest1 { public static void main(String args[]) { try { // 创建一个元素类型为String ,长度为10的数组 Object arr = Array.newInstance(String.class, 10); // 依次为arr数组中index为5、6的元素赋值 Array.set(arr, 5, "疯狂Java讲义"); Array.set(arr, 6, "轻量级Java EE企业应用实战"); // 依次取出arr数组中index为5、6的元素的值 Object book1 = Array.get(arr , 5); Object book2 = Array.get(arr , 6); // 输出arr数组中index为5、6的元素 System.out.println(book1); System.out.println(book2); } catch (Throwable e) { System.err.println(e); } } }
操作多维数组的例子:
public class ArrayTest2 { public static void main(String args[]) { /* 创建一个三维数组。 根据前面介绍数组时讲的:三维数组也是一维数组, 是数组元素是二维数组的一维数组, 因此可以认为arr是长度为3的一维数组 */ Object arr = Array.newInstance(String.class, 3, 4, 10); // 获取arr数组中index为2的元素,该元素应该是二维数组 Object arrObj = Array.get(arr, 2); // 使用Array为二维数组的数组元素赋值。二维数组的数组元素是一维数组, // 所以传入Array的set()方法的第三个参数是一维数组。 Array.set(arrObj , 2 , new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战" }); // 获取arrObj数组中index为3的元素,该元素应该是一维数组。 Object anArr = Array.get(arrObj, 3); Array.set(anArr , 8 , "疯狂Android讲义"); // 将arr强制类型转换为三维数组 String[][][] cast = (String[][][])arr; // 获取cast三维数组中指定元素的值 System.out.println(cast[2][3][8]); System.out.println(cast[2][2][0]); System.out.println(cast[2][2][1]); } }
18.5 使用反射生成JDK动态代理
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口,可生成JDK动态代理或动态代理对象.
动态代理的例子:
interface Person { void walk(); void sayHello(String name); } class MyInvokationHandler implements InvocationHandler { /* 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 其中: proxy:代表动态代理对象 method:代表正在执行的方法 args:代表调用目标方法时传入的实参。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("----正在执行的方法:" + method); if (args != null) { System.out.println("下面是执行该方法时传入的实参为:"); for (Object val : args) { System.out.println(val); } } else { System.out.println("调用该方法没有实参!"); } return null; } } public class ProxyTest { public static void main(String[] args) throws Exception { // 创建一个InvocationHandler对象 InvocationHandler handler = new MyInvokationHandler(); // 使用指定的InvocationHandler来生成一个动态代理对象 Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader() , new Class[]{Person.class}, handler); // 调用动态代理对象的walk()和sayHello()方法 p.walk(); p.sayHello("孙悟空"); } }
18.5.2 动态代理和AOP
一个AOP的实现例子:
public interface Dog { // info方法声明 void info(); // run方法声明 void run(); }
public class GunDog implements Dog { // 实现info()方法,仅仅打印一个字符串 public void info() { System.out.println("我是一只猎狗"); } // 实现run()方法,仅仅打印一个字符串 public void run() { System.out.println("我奔跑迅速"); } }
public class DogUtil { // 第一个拦截器方法 public void method1() { System.out.println("=====模拟第一个通用方法====="); } // 第二个拦截器方法 public void method2() { System.out.println("=====模拟通用方法二====="); } }
public class MyInvokationHandler implements InvocationHandler { // 需要被代理的对象 private Object target; public void setTarget(Object target) { this.target = target; } // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { DogUtil du = new DogUtil(); // 执行DogUtil对象中的method1。 du.method1(); // 以target作为主调来执行method方法 Object result = method.invoke(target , args); // 执行DogUtil对象中的method2。 du.method2(); return result; } }
public class MyProxyFactory { // 为指定target生成动态代理对象 public static Object getProxy(Object target) throws Exception { // 创建一个MyInvokationHandler对象 MyInvokationHandler handler = new MyInvokationHandler(); // 为MyInvokationHandler设置target对象 handler.setTarget(target); // 创建、并返回一个动态代理 return Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , handler); } }
public class Test { public static void main(String[] args) throws Exception { // 创建一个原始的GunDog对象,作为target Dog target = new GunDog(); // 以指定的target来创建动态代理 Dog dog = (Dog)MyProxyFactory.getProxy(target); dog.info(); dog.run(); } }
--------------结果----------------- =====模拟第一个通用方法===== 我是一只猎狗 =====模拟通用方法二===== =====模拟第一个通用方法===== 我奔跑迅速 =====模拟通用方法二=====
18.6 反射和泛型
例子1:泛型工厂类
public class CrazyitObjectFactory2 { public static <T> T getInstance(Class<T> cls) { try { return cls.newInstance(); } catch(Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { // 获取实例后无须类型转换 Date d = CrazyitObjectFactory2.getInstance(Date.class); JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class); } }
例子2:使用反射来获取泛型信息
public class GenericTest { private Map<String , Integer> score; public static void main(String[] args) throws Exception { Class<GenericTest> clazz = GenericTest.class; Field f = clazz.getDeclaredField("score"); // 直接使用getType()取出的类型只对普通类型的成员变量有效 Class<?> a = f.getType(); // 下面将看到仅输出java.util.Map System.out.println("score的类型是:" + a); // 获得成员变量f的泛型类型 Type gType = f.getGenericType(); // 如果gType类型是ParameterizedType对象 if(gType instanceof ParameterizedType) { // 强制类型转换 ParameterizedType pType = (ParameterizedType)gType; // 获取原始类型 Type rType = pType.getRawType(); System.out.println("原始类型是:" + rType); // 取得泛型类型的泛型参数 Type[] tArgs = pType.getActualTypeArguments(); System.out.println("泛型信息是:"); for (int i = 0; i < tArgs.length; i++) { System.out.println("第" + i + "个泛型类型是:" + tArgs[i]); } } else { System.out.println("获取泛型类型出错!"); } } }