-------android培训、java培训、期待与您交流! ----------
.自定义泛型方法的练习与类型推断总结
1.泛型方法的练习题
1)编写一个泛型方法,自动将Object类型的对象转换成其他类型。
2)定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
3)采用自定义泛型方法的方式打印出任意参数化类型的集合中的所有内容。
注:在这种情况下,前面的通配符方案要比泛型方法更有效,当一个类型变量
用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量
在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅
在签名的时候使用,才需要使用泛型方法。
4)定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
5)定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另
一个数组中。
2.类型推断总结:
1)编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉
推断的,其实现方法是一种非常复杂的过程。
2)根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
<1>当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么
根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接
根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],1,2) ---> static <E> void swap(E[] a,int i,int j)
<2>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用
方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,
例如:
add(3,5) ---> static <T> T add(T a,T b)
<3>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法
时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的
最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出
问题:
fill(new Integer[3],3.5f) ---> static <T> void fill(T[] a,T v)
<4>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时
这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,
例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,
对比eclopse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x = add(3,3.5f) ---> static <T> T add(T a,T b)
<5>参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,
而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) ---> static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(),new Integer[5]) ---> statoc <T> void copy(Collection<T> a,T[] b);
42.自定义泛型类的应用
1.定义泛型类型
1)如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型
要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别
的泛型,语法格式如下:
public class GenericDao<T>
{
private T field1;
public void save(T obj){}
public T getById(int id){}
}
2)类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,
如下两种方式都可以:
GenericDao<String> dao = null;
new GenericDao<String>();
3)注意:
<1>在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
<2>当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能
被静态变量和静态方法调用,因为静态成员是被所有参数化的类所共享的,所以静态
成员不应该有类级别的类型参数。
4)问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?
43.通过反射获得泛型的实际类型参数
1.Method类(java.lang.reflect包)中方法:
Type[] getGenericParameterTypes()
按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的。JDK1.5
Type getGenericReturnType()
返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。
Type[] getGenericExceptionTypes()
返回 Type 对象数组,这些对象描述了声明由此 Method 对象抛出的异常。
Class<?>[] getParameterTypes()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。
Class<?> getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
TypeVariable<Method>[] getTypeParameters()
返回 TypeVariable 对象的数组,这些对象描述了由 GenericDeclaration 对象表示的一般声明按声明顺序来声明的类型变量。
2.接口Type(java.lang.reflect包)--->ParameterizedType
方法摘要
Type[] getActualTypeArguments()
返回表示此类型实际类型参数的 Type 对象的数组。 实际类型参数
Type getOwnerType()
返回 Type 对象,表示此类型是其成员之一的类型。
Type getRawType()
返回 Type 对象,表示声明此类型的类或接口。 原始类型
44.类加载器及其委托机制的深入分析
1.类加载器
1)类加载器及其作用:
字节码的原始信息存放在硬盘上的classpath指定的目录下,java程序用到某个类,虚拟机要先
将该类的字节码加载到内存里,进行处理后得到的就是字节码。实现这个过程的机制就是类加载器,
其作用就是加载类。
2)类加载器BootStrap
类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有
第一个不是java类的类加载器,它就是BootStrap.
BootStrp是JVM里的第一个类加载器,它不是java类,不需要被加载,它是嵌套在JVM内核里的,
它是用C++写的二进制代码。
3)java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定
位置的类:
BootStrap,ExtClassLoader,AppClassLoader
4)类加载器的树形结构
BootStrap -------> JRE/lib/rt.jar
|
ExtClassLoader -----> JRE/lib/ext/*.jar
|
AppClassLoader -----> CLASSPATH指定的所有jar或目录
/ \
MyClassLoader ItcastClassLoader --> 传智播客指定的特殊目录
注:每个ClassLoader本身只能分别加载特定位置和目录中的类。
<1>用eclipse的打包工具将ClassLoaderTest输出成jre/lib/ext 目录下的itcast.jar包
ClassLoaderTest.java上右键-->Export(输出)-->java--JAR file-->Next-->
JAR file:c:\java\jdk1.6\jre\lib\ext\itcast.jar-->finish
<2>这时classpath目录和ext/itcast.jar包中都有ClassLoaderTest.class,但运行结果却是:
ExtClassLoader,这就有一个优先级的问题。
2.类加载器的委托加载机制
1)当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
<1>首先当前线程的类加载器去加载线程中的第一个类。
<2>如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B。
<3>还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
2)每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者加载器,还加载不了,则抛出ClassNoFoundException,
而不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多儿子,找哪一个呢?
3.面试题:能不能自己写个类叫java.lang.System?
写了也加载不到,因为java中的类加载器采用委托机制,总是保证父类类加载器优先,也就是
总是使用父类们能找到的类,这样就总是使用java系统提供的System.除非自己写个类加载器
来加载自己指定目录下的类。
4.补充:
1)Thread类中方法:
ClassLoader getContextClassLoader()
返回该线程的上下文 ClassLoader。
void setContextClassLoader(ClassLoader cl)
设置该线程的上下文 ClassLoader。
2)ClassLoader类(java.lang)
<1>类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。
每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建
<2>构造方法摘要
protected ClassLoader()
使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器。
protected ClassLoader(ClassLoader parent)
使用指定的、用于委托操作的父类加载器创建新的类加载器。
ClassLoader getParent()
返回委托的父类加载器。
<3>方法摘要:
Class<?> loadClass(String name)
使用指定的二进制名称来加载类。
protected Class<?> loadClass(String name, boolean resolve)
使用指定的二进制名称来加载类。
3) Class类中方法:
ClassLoader getClassLoader()
返回该类的类加载器。
45.自定义类加载器的编写原理分析:
1.原理分析:
ClassLoader中的loadClass()方法是保证委托机制流程的方法,故只能继承,不能复写,查找类要用到
的findClass需要复写,将得到的class文件的内容转成字节码的方法是defineClass().也不需要复写。
2.类 ClassLoader(java.lang)
1)构造方法摘要
protected ClassLoader()
使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器。
protected ClassLoader(ClassLoader parent)
使用指定的、用于委托操作的父类加载器创建新的类加载器。
2)方法摘要
protected Class<?> defineClass(byte[] b, int off, int len)
已过时。 由 defineClass(String, byte[], int, int) 取代
protected Class<?> defineClass(String name, byte[] b, int off, int len)
将一个 byte 数组转换为 Class 类的实例。
protected Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)
使用可选的 ProtectionDomain 将一个 byte 数组转换为 Class 类的实例。
protected Class<?> defineClass(String name, ByteBuffer b, ProtectionDomain protectionDomain)
使用可选的 ProtectionDomain 将 ByteBuffer 转换为 Class 类的实例。
protected Class<?> findClass(String name)
使用指定的二进制名称查找类。
protected Class<?> findLoadedClass(String name)
如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。
ClassLoader getParent()
返回委托的父类加载器。
static ClassLoader getSystemClassLoader()
返回委托的系统类加载器。
Class<?> loadClass(String name)
使用指定的二进制名称来加载类。
protected Class<?> loadClass(String name, boolean resolve)
使用指定的二进制名称来加载类。
46.编写对class文件进行加密的工具类
1.编写自己的类加载器
1)知识讲解:
<1>自定义的类加载器必须继承ClassLoader
<2>loadClass方法与findClass方法
<3>defineClass方法
2)编程步骤:
<1>编写一个对文件内容进行简单加密的程序。
<2>编写了一个自己的类加载器,可实现对加密过的类进行装载和解密。
<3>编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,
因为编译器无法识别这个类,程序中可以除了使用ClassLoader.load方法之外,
还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName.
3)实验步骤:
<1>对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如:
java MyClassLoader MyTest.class F:\itcast
<2>运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为
AppClassLoader:java MyClassLoader MyTest F:\itcast
<3>用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,
错误说明是AppClassLoader类加载器失败。
<4>删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
2.在eclipse中编写代码的实践操作步骤:
1)创建类加载器MyClassLoader类,同时也作为加密工具。
2)创建要被加密的类ClassLoaderAttachment(设置其父类为Date).
3)创建新文件夹itcastlib,用于放置加密后的文件:
工程-->右键-->New-->Folder-->Folder name:itcastlib-->Finish
4)设置类加载器MyClassLoader的运行参数:
右键-->Run As-->Run Configurations-->Arguments-->Program arguments:分别为ClassLoaderAttachment
所在的路径(要加引号)及加密后放置文件的目录:itcastlib
注:同时检查主函数Main的设置是不是当前类
获得路径的简单方式:打开方件所在目录-->打开运行-->将文件拉到运行中复制即可。
5)在ClassLoaderTest类中运行ClassLoaderAttachment类的toString()方法--->hello,itcast
6)用itcastlib文件夹中的加密后的ClassLoaderAttachment.class 复盖掉加密前的同名该文件。
7)再在CLassLoaderTest类中运行,则报错xception in thread "main" java.lang.ClassFormatError: Incompatible magic value
47.编写和测试自己编写的解密类加载器
1.相关知识点:
1)ByteArrayOutputStream类(java.io)
<1>此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。
可使用 toByteArray() 和 toString() 获取数据。 关闭 ByteArrayOutputStream 无效。
此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException
<2>构造方法摘要
ByteArrayOutputStream()
创建一个新的 byte 数组输出流。
ByteArrayOutputStream(int size)
创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。
<3>方法摘要
void close()
关闭 ByteArrayOutputStream 无效。
byte[] toByteArray()
创建一个新分配的 byte 数组。
String toString()
使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此 byte 数组输出流。
void write(int b)
将指定的字节写入此 byte 数组输出流。
2)类 ByteArrayInputStream(java.io)
<1>ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。
内部计数器跟踪 read 方法要提供的下一个字节。 关闭 ByteArrayInputStream 无效。
此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
<2>构造方法摘要
ByteArrayInputStream(byte[] buf)
创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。
ByteArrayInputStream(byte[] buf, int offset, int length)
创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。
<3>方法摘要
void close()
关闭 ByteArrayInputStream 无效。
int read()
从此输入流中读取下一个数据字节。
int read(byte[] b, int off, int len)
将最多 len 个数据字节从此输入流读入 byte 数组。
3)loadClass()会自动调用findClass()方法。
48.类加载器的一个高级问题的实验分析
在eclipse中的演示步骤:
1.创建web项目itcastweb
右键--->New-->Web Project-->itcastweb
2.创建web中的类Servlet(web中的类是特殊的类,其父类是Servlet,故可直接创建Servlet)
在src上右键-->New-->Servlet-->
package:cn.itcast.itcastweb.web.servlets(界面层)
Name: MyServlet
Superclass:javax.servlet.http.HttpServlet
(只勾选doGet做演示)
3.配置服务器
选择web工程-->点击Project Deployments(在工具栏环表箭头的图标)-->Add-->Server:Tomcat 6.x
-->Finish-->OK
(配置完后在目录apache-tomcat-6.0\webapps下就有itcastweb文件夹)
4.启动Tomcat,即双击startup.bat
5.在浏览器中访问Tomcat服务器
http://localhost:8080/itcastweb/servlet/MyServlet(这是项目内的路径)
(查看内容:itcastweb-->webRoot-->WEB-INF-->web.xml-->source)
-->访问的结果打印到了后台
6.重载Tomcat,则会在浏览器显示Tomcat自己的类加载器:WebappClassLoader
7.将MyServlet.java打成jar包输出到JDK下
MyServlet.java-->右键-->Export-->java--JAR file-->Next-->Next-->Finish
(输出的JDK版本要和Tomcat配置的JDK一致,可查看Tomcat的配置,即将startup.bat放在
编辑器中打开:set JAVA_HOME=c:\java\JDK6.0)
8.重启Tomcat
9.在浏览器中再次查看,报错:没找到HttpServlet.
(这是因为,这时的发起者加载器是ExtClassLoader,而HttpServlet不在Ext目录下)
10.将HttpServlet的jar包也放在Ext目录下。
HttpServlet是Tomcat提供的,在apache-tomcat-6.0\lib\servlet-api.jar,复制到JDK6.0\jre\lib\ext下。
11.重启Tomcat(即将JVM重启)
12.再在浏览器中访问
此时显示的加载器便是ExtClassLoader了。
注:要特别掌握配置服务器
49.分析代理类的作用与原理及AOP概念
1.代理的概念与作用
1)生活中的代理:
eg:电脑的代理商的例子
2)程序中的代理
<1>要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,
例如,异常处理,日志,计算方法的运行时间,事务管理,等等,怎么做呢?
<2>编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的
相同方法,并在调用方法时加上系统功能的代码。(参看原理图)
<3>如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,
在配置文件中配置是使用目标类,还是代理类,这样以后很容易切换,譬如,
想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,
以后运行一段时间后,又想去掉系统功能也很容易。eg.在ReflectTest2.java中
通过修改配置文件config.properties中的className = java.util.HashSet就可实现
代理类和目标类的切换。
3)程序代理代码举例:
class X
{
void sayHello()
{
System.out.println("hello,itcast");
}
}
class XProxy
{
void sayHello()
{
int starttime;
X.sayHello();
int endtime;
}
}
4)程序代理架构图:
Client 接口
客户端调用程序 doSomeThing()
| / ^
| / |
| / |
v / |
Proxy Target
代理类 ------------------> 目标类
doSomeThing() doSomeThing()
{ {
//前置系统功能代码 //业务功能代码
目标对象.doSomeThing() ----> }
//后置系统功能代码
}
2.AOP
1)系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService----|--------|-------|------
CourseService ----|--------|-------|------
MiscService ----|--------|-------|------
2)用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
-----------------------------------------------切面
...... ...... ......
-----------------------------------------------切面
} } }
3)交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),
AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,
这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
----------------------------------------------切面
func1 func2 func3
{ [ {
..... .... ....
} } }
----------------------------------------------切面
4)安全,事务,日志等功能要贯穿到好多个模块中,所以,它们是交叉业务。
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
3.动态代理技术
1)要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态
代理方式,将是一件非常麻烦的事情!因为要写成百上千个代理类,就太累了。
2)JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,
即动态代理类。
3)JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作
具有相同接口的目标类的代理。
4)CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,
如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
5)代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,
还可以在代理方法中的如下四个位置加上系统功能代码:
<1>在调用目标方法之前
<2>在调用目标方法之后
<3>在调用目标方法前后
<4>在处理目标方法异常的catch块中
void sayHello()
{
......
try
{
target.sayHello();
}
catch(Exception e)
{
.......
}
......
}
50.创建动态类及查看其方法列表信息
1.类 Proxy(java.lang.reflect )
1)Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,
该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。
每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。
通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,
并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。
调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果
返回。
2)字段摘要
protected InvocationHandler h
此代理实例的调用处理程序。
3)构造方法摘要
protected Proxy(InvocationHandler h)
使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
4)方法摘要
static InvocationHandler getInvocationHandler(Object proxy)
返回指定代理实例的调用处理程序。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
static boolean isProxyClass(Class<?> cl)
当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
2.补充:StringBuilder与StringBuffer的区别
在应用上基本一样,都是往字符串上动态地添加内容,单线程时用StringBuilder,因为它不用考虑
线程的安全问题,效率高些。多线程时用StringBuffer会比较安全些。
****这就如同走路一样,不安全的车道上左顾右看,行使慢,要是安全的单行道,就不用考虑安全问题,
速度就快。
以下是我的程序代码:
/*
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
@interface Meta{
String v();
}
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value= {ElementType.METHOD,ElementType.TYPE})
@interface ItcastAnnotation{
String name();
int age() default 100;
int[] arr() default {0,00,0};
String[] str() default{"fff","yyy","ggg"};
TrafficLamp lamp() default TrafficLamp.RED;
Meta value() default @Meta(v="Meta");
}
@ItcastAnnotation(name="付玉光",age=200)
@SuppressWarnings("deprecation")
class AnnotationDemo{
public static void main(String args[])throws Exception{
System.runFinalizersOnExit(true);
sayHello();
if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class)){
ItcastAnnotation annotation = AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.age());
System.out.println(Arrays.asList(annotation.arr().length));
System.out.println(annotation.str());
System.out.println(annotation.lamp().name());
System.out.println(annotation.value().v());
}
}
@ItcastAnnotation(name=".....123")
@Deprecated
public static void sayHello()throws Exception{
System.out.println("ABC ......123.");
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationDemo.class.getMethod("sayHello").getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.age());
System.out.println(annotation.arr().length);
System.out.println(Arrays.asList(annotation.str()));
}
@Override
public boolean equals(Object o){
System.out.println(o);
return false;
}
}
*/
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
//定义一个Annotaion;
@Retention(value=RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface ItAnnotation{
String value() default "wfy";
int[] intArr();
TrafficLamp lamp();
myAnnotation myA();
}
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
Class clazz() default String.class;
}
@ItAnnotation(value="付玉光",intArr={1,2,3},lamp=TrafficLamp.RED,myA=@myAnnotation(clazz=String.class))
@SuppressWarnings("deprecation")//压制安全警告信息。
class AnnotationDemo{
public static void main(String args[]){
//System.runFinalizersOnExit(true);
//sayHello();
if(AnnotationDemo.class.isAnnotationPresent(ItAnnotation.class)){
ItAnnotation it = (ItAnnotation)AnnotationDemo.class.getAnnotation(ItAnnotation.class);
System.out.println(it.value());
System.out.println(it.intArr().length);
System.out.println(it.lamp());
System.out.println(it.myA());
}
}
@Deprecated
public static void sayHello(){
System.out.println("付玉光,你好");
}
@Override//覆写父类中的方法。
public boolean equals(Object o){
return false;
}
}
enum TrafficLamp{
RED(30){
public TrafficLamp nextLamp(){
return GREEN;
}
},
GREEN(45){
public TrafficLamp nextLamp(){
return YELLOW;
}
},
YELLOW(15){
public TrafficLamp nextLamp(){
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;
TrafficLamp(int time){
this.time = time;
}
}