Java反射机制的笔记

第一次更改:修复了反编译部分格式混乱的问题 

第二次更改:解决了流没关掉的问题,加入的与注解联合使用的笔记,注解的相关笔记

                      问题:运行之后需要手动结束,不会随主方法结束而结束

                                 如果有知道原因的大佬请指出并提供改正方案谢谢

分享一下自己做的反射机制笔记

要是觉得做的好请多多支持

有错误的或有更好的方法请大家提出来,一起进步ovo

写在评论里就好ovo

由于整理的时候涉及大量的复制粘贴,所以注释可能有混乱的地方

我检查了一遍,如果有发现的也请提出

中间还有关于两种转码 / 规避乱码的方式,所以测试时请不要把编码方式设置为UTF-8

(因为我的eclipse默认是GBK啊我好难)

目录

反射源代码(笔记)部分

注解源代码(笔记)部分

运行结果应该是这样的 


反射源代码(笔记)部分

package learning;

import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Timer;

public class Reflection {
	//反射机制
	//可以读和修改字节码文件(.class)
	/*
	 * 重要的类:
	 * java.lang.Class					整个字节码
	 * java.lang.reflect.Method			方法字节码
	 * java.lang.reflect.Constructor	构造方法字节码
	 * java.lang.reflect.Field			属性字节码
	 */
	@SuppressWarnings({ "rawtypes", "unused", "deprecation" })
	public static void main(String[] args) {
		/** 第一个操作:获取Class */
		//方法一
		//静态方法,实参是完整包名+类名,可能找不到
		//用这个可以实现只执行静态代码块,因为这个方法的执行导致类加载
		Class c1 = null;
		try {
			c1 = Class.forName("learning.TestReflection");
			System.out.println("full name: " + c1.getName());
			System.out.println("simple name: " + c1.getSimpleName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		//方法二
		//Java任何一个引用数据类型都有的getClass()
		TestReflection tr = new TestReflection("testing", 0xabcd);
		Class<?> c2 =tr.getClass();
		//而且,
		System.out.println("c1和c2一样:" + (c1 == c2));
		//因为字节码文件装载到JVM中时,只装一个
		
		//方法三
		//Java任何一个 → 类型 ← 都有属性class
		Class<TestReflection> c31 = TestReflection.class;
		Class<Integer> c32 = int.class;
		
		
		/** 第二个操作:通过Class来实例化对象 */
		//方法一
		//newInstance()方法会自动调用类的无参构造器,创建对象
		//要是没无参就给你报异常
		Object obj = null;
		try {
			obj = c1.newInstance();
			System.out.println("is TestReflection: " 
					+ (obj instanceof TestReflection));
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		//利用属性配置文件来创建对象
		//这种方式比较灵活(符合OCP开闭原则,对扩展开放,对修改关闭)
		Properties pro = new Properties();
		FileReader fr = null;
		try {
			fr = new FileReader("F:\\learning\\src\\learning\\"
					+ "use for testing reflection.properties");
			pro.load(fr);
			Class cpro = Class.forName(pro.getProperty("class"));
			Object cobj = cpro.newInstance();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}finally {
			if(fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		
		/** 第三个操作:获取类路径下文件的绝对路径 */
		//这样即使代码移植了也不会有影响,是通用的
		//方式在src下的都是在类路径下,这时可以这种方法
		//src是类的根路径
		/*
		 * 努力一下,把它记住
		 * Thread.currentThread();当前线程对象
		 * getContextClassLoader();获取当前线程的类加载器对象
		 * getResource(String name);当前线程类加载器默认从src下加载资源,返回URL
		 * getPath();获取绝对路径
		 */
		//记得在括号里填src下的路径,不要直接写文件名
		//但是这样会乱码耶
		System.out.println(Thread.currentThread().
				getContextClassLoader().
				getResource("learning/use for testing reflection.properties").
				getPath());
		//不乱码方式一
		//用URLDecoder.decode(String s, String enc);方法重新编码
		//其实直接改properties改成UTF-8也可以,要不是因为笔记做太多了懒得改我才不用qwq
		try {
			System.out.println(URLDecoder.decode(Thread.currentThread().
					getContextClassLoader().
					getResource("learning/use for testing reflection.properties").
					getPath(), "UTF-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		//不乱码方式二
		//把返回的URL转换成URI
		try {
			System.out.println(Thread.currentThread().
					getContextClassLoader().
					getResource("learning/use for testing reflection.properties").
					toURI().getPath());
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		
		//资源绑定器
		//只能绑定属性配置文件,且文件必须在类路径下
		//写路径时,文件扩展名(.properties)不写
		ResourceBundle bundle = 
				ResourceBundle.getBundle("learning/use for testing reflection");
		String className = bundle.getString("class");
		System.out.println("资源绑定器:" + className);
		

		/** 第四个操作:和流联合使用 */
		String path = null;
		try {
			path = Thread.currentThread().
					getContextClassLoader().
					getResource("learning/use for testing reflection.properties").
					toURI().getPath();
		} catch (URISyntaxException e1) {
			e1.printStackTrace();
		}
		//可以用FileReader,不过还有一种方法
		FileReader properties = null;
		InputStream reader = null;
		try {
			properties = new FileReader(path);
			Properties test = new Properties();
			test.load(properties);
			//获取value
			System.out.println(test.getProperty("class"));
			//-----------------------------------------------------------
			//or
			//直接以流的形式返回
			reader = Thread.currentThread().
					getContextClassLoader().
					getResourceAsStream("learning/"
							+ "use for testing reflection.properties");
			test.load(reader);
			System.out.println(test.getProperty("class"));
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(properties != null) {
				try {
					properties.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		System.out.println("//===================================================");
		
		
		/** 第五个操作:获取Field */
		System.out.println("Field: ");
		//获取类中所有public修饰的Field
		Field[] Pfields = c1.getFields();
		System.out.println("length: " + Pfields.length);
		//好像长度是0哈
		//--------------------------------------------------------------------
		//获取所有的Field
		Field[] fields = c1.getDeclaredFields();
		System.out.println("length: " + fields.length);
		//啊好,有长度了,那来遍历一下
		for(Field f: fields) {
			System.out.println("//-------------------------------------");
			//获取整个变量
			System.out.println(f);
			//获取名字
			System.out.println("name: " + f.getName());
			//获取完整类型
			System.out.println("full type: " + f.getType());
			//获取简单类型
			System.out.println("simple type: " + f.getType().getSimpleName());
			//获取修饰符列表,返回一个数字,每个数字是修饰符列表的代号
			System.out.println("modifiers number: " + f.getModifiers());
			//获取修饰符列表的名称
			System.out.println("modifiers name: "
					+ Modifier.toString(f.getModifiers()));
		}
		
		System.out.println("//===================================================");
		
		
		
		/** 第六个操作:获取Method */
		System.out.println("Method: ");
		//获取类中所有的,所有的,所有的方法,包括private,但是没有构造器
		Method[] methods = c1.getDeclaredMethods();
		System.out.println("length: " + methods.length);
		//那来遍历一下
		for(Method m: methods) {
			System.out.println("//-------------------------------------");
			//获取整个变量
			System.out.println(m);
			//获取名字
			System.out.println("name: " + m.getName());
			//获取返回值类型
			System.out.println("full return type: " + m.getReturnType());
			//获取简单返回值类型
			System.out.println("simple return type: "
					+ m.getReturnType().getSimpleName());
			//获取修饰符列表,返回一个数字,每个数字是修饰符列表的代号
			System.out.println("modifiers number: " + m.getModifiers());
			//获取修饰符列表的名称
			System.out.println("modifiers name: "
					+ Modifier.toString(m.getModifiers()));
			//获取参数列表
			System.out.print("parameter types: ");
			Class[] parameterTypes = m.getParameterTypes();
			if(parameterTypes.length == 0) {
				System.out.print("null");
			}else {
				for(Class parameterType: parameterTypes) {
					System.out.print(parameterType.getSimpleName() + " ");
				}
			}
			System.out.println();
		}
		
		System.out.println("//===================================================");
		
		
		/** 第七个操作:获取Constructor */
		System.out.println("Constructor: ");
		//可以反射构造器了
		Constructor[] constructors = c2.getConstructors();
		//遍历
		for(Constructor c: constructors) {
			System.out.println("//-------------------------------------");
			//获取整个变量
			System.out.println(c);
			//获取名字
			System.out.println("name: " + c.getName());
			//获取修饰符列表,返回一个数字,每个数字是修饰符列表的代号
			System.out.println("modifiers number: " + c.getModifiers());
			//获取修饰符列表的名称
			System.out.println("modifiers name: "
					+ Modifier.toString(c.getModifiers()));
		}
		//操作五六七其实没有什么太大的区别,背出Method就好了
		System.out.println("//===================================================");
		
		
		/** 第八个操作:获取Annotation */
		System.out.println("Annotation: ");
		//注解也可以被反射(要加RUNTIME)
		
		//判断类上面有没有@Annotation注解
		System.out.println("has @annotation: "
				+ c2.isAnnotationPresent(Annotation.class));
		//如果有注解,获取该注解对象
		if(c2.isAnnotationPresent(Annotation.class)) {
			Annotation anno = c2.getAnnotation(Annotation.class);
			//获取名字
			System.out.println("the annotation on TestReflection: " + anno);
		}
		
		//同理,获取方法上的注解对象
		try {
			Method annoMethod = 
					c2.getMethod("printString");
			if(annoMethod.isAnnotationPresent(Annotation.class)) {
				Annotation anno = annoMethod.getAnnotation(Annotation.class);
				System.out.println("the annotation on " 
						+ annoMethod.getName() + ": " + anno);
			}
		} catch (NoSuchMethodException e2) {
			e2.printStackTrace();
		} catch (SecurityException e2) {
			e2.printStackTrace();
		}
		//其他都一样的啊都一样的
		System.out.println("//===================================================");
		
		
		/** 第九个操作:用反射访问类中的元素 */
		//这种方式的程序扩展性强
		
		//修改/获取属性的值
		for(Field f: fields) {
			try {
				//实例化TestReflection对象(重新实例化是因为之前实例化的距离太远看不到)
				obj = c2.newInstance();
				
				//获取change属性
				Field field = c2.getDeclaredField("change");
				//赋值(如果可以的话)
				field.set(obj, 142857);
				//获取
				if(f.getName() == "change") {
					System.out.println("change = " + field.get(obj));
				}
				
				//访问私有属性
				Field priField = c2.getDeclaredField("no");
				//顺便改改
				//首先要打破封装
				priField.setAccessible(true);
				priField.set(obj, 758241);
				//获取
				if(f.getName() == "no") {
					System.out.println("no = " + priField.get(obj));
				}
				//解释一下为啥先输出no再输出change
				//因为写类的时候no写在前面
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		}
		
		//通过反射机制调用方法
		//获取方法
		try {
			//设置名字
			Field name = c2.getDeclaredField("name");
			name.setAccessible(true);
			name.set(obj, "testing");
			//-------------------------------------------------------------------
			Method method = c2.getDeclaredMethod("printString", String.class);
			//调用方法
			method.invoke(obj, "qwq");
		} catch (NoSuchMethodException e1) {
			e1.printStackTrace();
		} catch (SecurityException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		
		//通过反射机制调用构造方法
		//调用无参构造器创建对象咱都会
		//要知道怎么调用有参数构造器
		try {
			Constructor cons = c2.getConstructor(String.class, int.class);
			//然后常规操作new对象
			try {
				Object objTR = cons.newInstance("Alexander", 67);
				//测试
				if(objTR instanceof TestReflection) {
					TestReflection trn = (TestReflection)objTR;
					System.out.println("fields: " + trn.getName() + ", "
							+ trn.getNo());
				}
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		} catch (NoSuchMethodException e1) {
			e1.printStackTrace();
		} catch (SecurityException e1) {
			e1.printStackTrace();
		}
		
		//通过反射机制访问注解
		Annotation anno = c2.getAnnotation(Annotation.class);
		//获取属性
		String[] values = anno.value();
		//哎是个数组,那遍历
		for(String value: values) {
			//用法和接口差不多
			System.out.println("element -> " + value);
		}
		
		System.out.println("//===================================================");
		
		
		/** 第十个操作:反编译 */
		StringBuilder sb = new StringBuilder();
		
		//反编译Annotation
		sb.append(c2.getAnnotation(Annotation.class));
		sb.append('\n');
		
		//反编译Class
		String classModifiers = Modifier.toString(c1.getModifiers());
		sb.append(classModifiers + "class " + c1.getSimpleName() + " {\n");
		
		//反编译Field
		sb.append("    //Field");
		for(Field f: fields) {
			sb.append('\n');
			sb.append("    ");
			sb.append(Modifier.toString(f.getModifiers())
					+ " " + f.getType().getSimpleName()
					+ " " + f.getName());
			//获取属性的值
			try {
				//获取序列化版本号
				Field serField = c2.getDeclaredField("serialVersionUID");
				//获取
				serField.setAccessible(true);
				if(f.getName() == "serialVersionUID") {
					sb.append(" = " + serField.get(obj) + "L");
				}
				
				//获取change属性
				Field field = c2.getDeclaredField("change");
				//获取
				if(f.getName() == "change") {
					sb.append(" = " + field.get(obj));
				}
				
				//访问私有属性
				Field priField = c2.getDeclaredField("no");
				priField.setAccessible(true);
				//获取
				if(f.getName() == "no") {
					sb.append(" = " + priField.get(obj));
				}
				
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			
			sb.append(';');
		}
		sb.append("\n\n");
		
		//反编译Method
		sb.append("    //Method\n");
		for(Method m: methods) {
			sb.append("    ");
			//前面的部分
			sb.append(Modifier.toString(m.getModifiers())
					+ " " + m.getReturnType().getSimpleName()
					+ " " + m.getName()
					+ "(");
			//打印实参列表
			Class[] parameterTypes = m.getParameterTypes();
			if(parameterTypes.length != 0) {//若长度不为零
				for(int i = 0; i < parameterTypes.length - 1; i++) {
					sb.append(parameterTypes[i].getSimpleName());
					sb.append(", ");
				}
				//最后一个参数不需要加", "所以单独输出
				sb.append(parameterTypes[parameterTypes.length - 1].getSimpleName());
				//另外一种输出方法:实参列表都输出完之后调用
				//sb.deleteCharAt(sb.length - 1);
				//再调用一次
				//sb.deleteCharAt(sb.length - 1);
				//同样可以去掉", "
				//别问为什么不用,问就是做笔记时候忘记了然后懒得改
			}
			sb.append(") {\n");
			sb.append("    }\n\n");
		}
		
		//反编译Constructor
		sb.append("    //Constructor\n");
		for(Constructor c: constructors) {
			sb.append("    ");
			//前面
			sb.append(Modifier.toString(c.getModifiers())
					+ " " + c1.getSimpleName()
					+ "(");
			//实参列表
			Class[] conPar = c.getParameterTypes();
			for(Class cPar: conPar) {
				sb.append(cPar.getSimpleName());
				sb.append(", ");
			}
			sb.deleteCharAt(sb.length() - 1);
			sb.deleteCharAt(sb.length() - 1);
			
			sb.append(") {\n    }\n");
		}
		
		sb.append('\n');
		sb.append("}");
		System.out.println(sb);
		
		
		/** 第十一个操作:获取父类,父接口 */
		//获取父类
		Class superClass  = c2.getSuperclass();
		System.out.println("super class: " + superClass.getSimpleName());
		//获取父接口
		Class[] superInterfaces = c2.getInterfaces();
		for(Class superInterface: superInterfaces) {
			System.out.println("不好意思啊就一个接口: "
					+ superInterface.getSimpleName());
		}
	}
}
//测试类(就不麻烦其他类了)
@Annotation({"class", "interesting"})
class TestReflection extends Timer implements Serializable{
	private static final long serialVersionUID = 1L;
	//静态代码块只在类加载时运行一次
	static {
		System.out.println("class TestReflection ready!!!");
	}
	
	private String name;
	private int no;
	@Annotation("field!")
	public int change = 100;
	
	public TestReflection() {
		//newInstance()自动调用无参构造器
		//测试下面内容时自行注释或解注释,不然很烦
//		System.out.println("new TestReflection: " + this.toString());
	}
	public TestReflection(String name, int no) {
		this.name = name;
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	
	private String returnString(String s1, String s2) {
		return s1 + s2;
	}
	@Annotation("method!")
	public void printString() {
		System.out.println(returnString(name, ": hey my dear!"));
	}
	public void printString(String str) {
		System.out.println(returnString(name, ": hey my dear! " + str));
	}
}

注解源代码(笔记)部分

package learning;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义注解

//加入元注解
@Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)//可以被反射
public @interface Annotation {
	//关于注解
	//效果去看learning.TestReflection
	/**
	 * 注解,或者叫注释类型(不是注释,而是注释类型)
	 * 注解可以出现在各种地方
	 * 
	 * 内置的注解
	 * Deprecated:
	 * 		凡是用@Deprecated注释的程序元素,并不鼓励使用这种元素,通常因为危险或有更好的选择
	 * 		表示某个元素已过时,向其他程序员传达信号,有更好的选择存在
	 * Override:
	 * 		表示一个方法重写父类方法(谁都会)
	 * 		只能注解方法,是给编译器参考的,和运行没有关系
	 * 		如果带有这个注解的方法不是重写父类的方法,编译器报错
	 * SuppressWarnings:
	 * 		指在注释元素中取消指定的编译器警告(谁都会)
	 * 
	 * 元注解:指用于标识注解类型的注解
	 * 常见的有Target, Retention
	 * 		Target注解用于标注注解可以出现在哪些位置上
	 * 		Retention注解用于标注注解最终保存在哪里
	 * 			@Retention(RetentionPolicy.SOURCE):保存在Java源文件中
	 * 			@Retention(RetentionPolicy.CLASS):保存在class文件中
	 * 			@Retention(RetentionPolicy.RUNTIME):保存在class文件中且可以被反射
	 * 
	 */
	
	//定义属性
	//看起来是方法,但我们叫它属性
	//当属性是value的时候属性名可以省略不写
	//如果一个注解里有属性,则必须给属性赋值
	//指定默认值后,这个属性可写可不写
	//不是数组的属性类型可以是:byte short int long float double boolean char String Class
	//属性也可以是上面类型的数组数组,枚举类型及其数组
	//如果数组中只有一个元素,则大括号可以省略不写
	String[] value() default "no!!!";
}

运行结果应该是这样的 

class TestReflection ready!!!
full name: learning.TestReflection
simple name: TestReflection
c1和c2一样:true
is TestReflection: true
/F:/learning/bin/learning/use%20for%20testing%20reflection.properties
/F:/learning/bin/learning/use for testing reflection.properties
/F:/learning/bin/learning/use for testing reflection.properties
资源绑定器:learning.TestReflection
learning.TestReflection
learning.TestReflection
//===================================================
Field: 
length: 1
length: 4
//-------------------------------------
private static final long learning.TestReflection.serialVersionUID
name: serialVersionUID
full type: long
simple type: long
modifiers number: 26
modifiers name: private static final
//-------------------------------------
private java.lang.String learning.TestReflection.name
name: name
full type: class java.lang.String
simple type: String
modifiers number: 2
modifiers name: private
//-------------------------------------
private int learning.TestReflection.no
name: no
full type: int
simple type: int
modifiers number: 2
modifiers name: private
//-------------------------------------
public int learning.TestReflection.change
name: change
full type: int
simple type: int
modifiers number: 1
modifiers name: public
//===================================================
Method: 
length: 7
//-------------------------------------
public java.lang.String learning.TestReflection.getName()
name: getName
full return type: class java.lang.String
simple return type: String
modifiers number: 1
modifiers name: public
parameter types: null
//-------------------------------------
public void learning.TestReflection.setName(java.lang.String)
name: setName
full return type: void
simple return type: void
modifiers number: 1
modifiers name: public
parameter types: String 
//-------------------------------------
public void learning.TestReflection.printString()
name: printString
full return type: void
simple return type: void
modifiers number: 1
modifiers name: public
parameter types: null
//-------------------------------------
public void learning.TestReflection.printString(java.lang.String)
name: printString
full return type: void
simple return type: void
modifiers number: 1
modifiers name: public
parameter types: String 
//-------------------------------------
public int learning.TestReflection.getNo()
name: getNo
full return type: int
simple return type: int
modifiers number: 1
modifiers name: public
parameter types: null
//-------------------------------------
public void learning.TestReflection.setNo(int)
name: setNo
full return type: void
simple return type: void
modifiers number: 1
modifiers name: public
parameter types: int 
//-------------------------------------
private java.lang.String learning.TestReflection.returnString(java.lang.String,java.lang.String)
name: returnString
full return type: class java.lang.String
simple return type: String
modifiers number: 2
modifiers name: private
parameter types: String String 
//===================================================
Constructor: 
//-------------------------------------
public learning.TestReflection(java.lang.String,int)
name: learning.TestReflection
modifiers number: 1
modifiers name: public
//-------------------------------------
public learning.TestReflection()
name: learning.TestReflection
modifiers number: 1
modifiers name: public
//===================================================
Annotation: 
has @annotation: true
the annotation on TestReflection: @learning.Annotation({"class", "interesting"})
the annotation on printString: @learning.Annotation({"method!"})
//===================================================
no = 758241
change = 142857
testing: hey my dear! qwq
fields: Alexander, 67
element -> class
element -> interesting
//===================================================
@learning.Annotation({"class", "interesting"})
class TestReflection {
    //Field
    private static final long serialVersionUID = 1L;
    private String name;
    private int no = 758241;
    public int change = 142857;

    //Method
    public String getName() {
    }

    public void setName(String) {
    }

    public void printString() {
    }

    public void printString(String) {
    }

    public int getNo() {
    }

    public void setNo(int) {
    }

    private String returnString(String, String) {
    }

    //Constructor
    public TestReflection(String, int) {
    }
    public TestReflectio) {
    }

}
super class: Timer
不好意思啊就一个接口: Serializable

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值