文章目录
1.1 动态编程(通过反射读写类&方法)
准备:配置文件+创建调用类
自定义的,类和方法用标识符分开就行
cn.edut.com.tarena.A ; a
cn.edut.com.tarena.B;b
调用类
package cn.edut.com.tarena;
public class A {
//@Test(value = "测试", paraClass = { String.class , Integer.class})
public void a() {
System.out.println("好嗨喔");
}
}
package cn.edut.com.tarena;
public class B {
public void b() {
System.out.println("好嗨嗨嗨嗨嗨喔!!!");
}
}
配置
重点
- 获得、解析URL
- IO流获得URL信息
package cn.edut.com.tarena;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
public class Runner {
private static List<String> list = new ArrayList<>();
static {
try {
/* * "/" --- Runner类文件存放的目录,即bin "/config.tst" --- bin目录/config.txt */
URL url = Runner.class.getResource("/config.txt");
String path = url.getPath();
// "d:/中/bin/config.txt" ---URL编码---> "d:/%E4%B8%AD/bin/config.txt"
// ---编码---> "d:/中/bin/config.txt"
// decode 解码
path = URLDecoder.decode(path, "utf-8");
System.out.println(path);
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), "GBK"));
String line;
while (null != (line = in.readLine())) {
// line = line.replace("\\s+", "");
line = line.trim();
list.add(line);
}
in.close();
System.out.println("配置成功 ! ");
} catch (Exception e) {
System.out.println("配置失败...");
e.printStackTrace();
}
}
public static void main(String[] args) {
}
}
反射实现方法调用
实现代码
package cn.edut.com.tarena;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import com.sun.xml.internal.ws.runtime.config.TubelineFeatureReader;
import sun.launcher.resources.launcher;
public class Runner {
private static List<String> list = new ArrayList<>();
static {
try {
/* * "/" --- Runner类文件存放的目录,即bin "/config.tst" * --- bin目录/config.txt */
URL url = Runner.class.getResource("/config.txt");
String path = url.getPath();
// "d:/中/bin/config.txt" ---URL编码---> "d:/%E4%B8%AD/bin/config.txt"
// ---编码---> "d:/中/bin/config.txt"
// decode 解码
path = URLDecoder.decode(path, "utf-8");
System.out.println(path);
BufferedReader in = new BufferedReader(
new InputStreamReader(
new FileInputStream(path), "GBK"));
String line;
while (null != (line = in.readLine())) {
//注意:这里用replace,无法去除多个空格
line = line.replaceAll("\\s+", "");
list.add(line);
}
in.close();
System.out.println("配置成功 ! ");
} catch (Exception e) {
System.out.println("配置失败...");
e.printStackTrace();
}
}
private static void launch() throws Exception {
for(String l : list ) {
String[] a = l.split(";");
Class<?> clazz = Class.forName(a[0]);
Method method = clazz.getDeclaredMethod(a[1]);
method.setAccessible(true);
method.invoke(clazz.newInstance()) ;
}
}
public static void main(String[] args) throws Exception {
launch();
}
}
1.2 注解
注解为其他开发工具,或其他java程序,来提供代码的额外信息
@Override
public String toString() {
...
}
- Override注解,告诉编译器,这里实在重写一个方法,让编译器来检查,重写语法是否正确
- 自定义注解,要自己编写处理代码
1.2.1 概念
注解很厉害哦,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。
<mark>它们被广泛应用于三大框架底层。</mark>
传统我们通过xml文本文件声明方式,
而现在最主流的开发都是基于注解方式,代码量少,
<mark>框架可以根据注解去自动生成很多代码</mark>,从而减少代码量,程序更易读。
例如最火爆的SpringBoot就完全基于注解技术实现。
注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。
有了java代码干嘛还要有@注解呢?
但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。
大家慢慢体会吧。
<mark>常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解</mark>。
听起来好绕,别着急,做两个例子,立刻清晰。
现在现有“<mark>元注解</mark>”这个概念。
1.2.2 分类
- JDK自带注解
- <mark>元注解</mark>
- 自定义注解
1.2.3 JDK注解
JDK注解的注解,就5个:
- <mark>@Override</mark>
- @Deprecated - 标记就表明这个方法已经过时了。 但我就要用,别提示我过期
- @SuppressWarnings(“deprecation”) - 忽略警告
- @SafeVarargs - jdk1.7出现,堆污染,不常用
- @FunctionallInterface jdk1.8出现,<mark>配合函数式编程拉姆达表达式</mark>,不常用
1.2.4 ∗∗元注解∗∗
描述注解的注解,就5个:
- <mark>@Target</mark> 注解用在哪里:类上、方法上、属性上
- <mark>@Retention</mark> 注解的声明周期:源文件中、class文件中、运行中
- @Inherited 允许子注解继承
- @Documented 生成javadoc时会包含注解,不常用
- @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
1.3 ∗∗元注解∗∗
1.3.1 @Target
Target 目标
描述注解的使用范围:
- ElementType.ANNOTATION_TYPE 应用于注释类型
- ElementType.CONSTRUCTOR 应用于构造函数
- <mark>ElementType.FIELD 应用于字段或属性</mark>
- ElementType.LOCAL_VARIABLE 应用于局部变量
- <mark>ElementType.METHOD 应用于方法级</mark>
- ElementType.PACKAGE 应用于包声明
- ElementType.PARAMETER 应用于方法的参数
- <mark>ElementType.TYPE 应用于类的任何元素(也可以用于类上 - Spring就这么用)</mark>
1.3.2 @Retention
<mark>定义了该注解被保留的时间长短,</mark>
- 某些注解仅出现在源代码中,而被编译器丢弃;
- 而另一些却被编译在class文件中(编译在class文件中的注解可能会被虚拟机忽略)
- 而另一些在class被装载时将被读取。
为何要分有没有呢?
没有时,反射就拿不到,从而就无法去识别处理。
- SOURCE 在源文件中有效(即源文件保留)
- CLASS 在class文件中有效(即class保留)
- <mark>RUNTIME 在运行时有效(即运行时保留)</mark> (通常用这个)
1.4 自定义注解 - 例子
自定义Test注解
package cn.edut.com.tarena.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/* * 元注解:对注解的注解 * * @Target 设置注解目标:类、方法、成员变量、参数变量... * @Retention 保留范围:源码、字节码、运行期内存 * 源码:编译成字节码时被丢弃 * 字节码:类被加载到内存时丢弃 * 运行期内存:在内存中保留 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
//如果没有默认值,使用注解时,就必须赋值
//有默认值,可以不赋值
int id() default 0 ;
String title() default "" ;
/* * 特殊属性名 value * 有特殊待遇,单独赋值时,可以不写value= * 可以作为一个有意义的属性名别名 * @Test("adfasf") * @Test(id=5, value="adfasf" ) * @Test(id=5, title="adfasf" ) */
//title的别名
String value() default "" ;
}
A类 - 用于测试
package cn.edut.com.tarena.test;
public class A {
@Test(value="value.a",title="title.A.a",id=1)
private void a() {
System.out.println("A.a");
}
@Test("value.b")
private void b() {
System.out.println("A.b");
}
//@Test(title="title.C" , "value.c") 报错
private void c() {
System.out.println("A.c");
}
@Test(title="title.d", id=4)
private void d() {
System.out.println("A.d");
}
}
Runner - 扫描自定义注解
运行相应的方法
package cn.edut.com.tarena.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Runner {
//在指定的类中,自动发现自动运行有@Test注解的方法
public static void main(String[] args) throws Exception {
Runner.lunch(A.class);
}
private static <T> void lunch(Class<T> c) throws Exception {
T obj = c.newInstance();
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
//在方法上,是否存在Test类型的注解
if(method.isAnnotationPresent(Test.class)) {
//获取 Test 注解数据
Test t = method.getAnnotation(Test.class);
System.out.println(t.id()+"-"+t.title()+"-"+t.value());
method.invoke(obj);
System.out.println("-----------------");
}
}
}
}