Java JDK-1.5高级特性

----------------------------------------------------- 静态导入 -----------------------------------------------------

静态导入在 import 后边多加一个 static

import java.lang.Math.*  表示导入 Math 类下的子类

import static java.lang.Math.*  静态导入的含义是导入 Math 类下的所有方法

 

所以如果 import java.lang.Math.*;  这样导入的话,那么

public static void main(String args[]){

System. out .println(min (3,9)); 比较两个值中的最小值

System.out.println(abs(3-9)); 返回两个数相减结果的绝对值

}

这两个方法就默认为不可识别的方法,必须静态导入 Math

 

 

 

------------------------------------------- 可变参数与 for 循环增强 --------------------------------------------

一个方法可以根据需求利用 可变参数这种功能 接收任意个参数,可变参数本身是一个数组,通过迭代或循环来使用多个参数

package com.VaryingParameter;

public class ParameterTest {

 

    public static int add( int x, int ... args){  

// 可变参数的格式为:数据类型 ...args

       int sum=x;

       for ( int i=0;i<args. length ;i++){  // 两种循环实现都可以

           sum+=args[i];  // 这里的 args 相当于一个数组,接收了所有请求过来的参数

       }

       /*for(int arg:args){  //for 循环增强,也叫做 foreach 循环

           sum+=arg;

       }*/

       return sum;

    }

    public static void main(String args[]){

       System. out .println(add (1,2));

       System. out .println(add (1,2,3));

       System. out .println(add (1,2,3,4));

    }

}

 

----------------------------------------------------- 枚举 -----------------------------------------------------

静态变量的方式在开发阶段无法防止一些非法值,让编译器就可以控制源程序中填写的非法值,例如,假设 1-7 表示星期几,有人不知道,可能会出现 int weekday = 0; 的情况。枚举就是要让一个类型变量的取值为若干个固定的值,否则,编译器就会报错。首先,大家要清楚枚举是一种特殊的类。

枚举中的成员就是枚举类的一个实例对象

枚举就相当于一个类,其中也可以定义抽象方法,枚举的每个成员分别由枚举类的子类来生成实例

带构造方法的枚举,构造方法必须定义成私有的

 

 

 

 

 

 

 

 

----------------------------------------------------- 反射 -----------------------------------------------------

反射就是把 Java 类中的各种成分映射成相应的 java

 

反射的作用 ----- 实现框架功能

1. 框架解决的核心问题:

我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进入我提供的框架中,框架与工具类有区别,你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的,工具类被用户的类调用,而框架则是调用用户提供的类

2. 框架要解决的核心问题:我在写框架时,你这个用户可能还在上小学,还不会写程序呢,我写的框架横须怎样能调用到你以后写的类呢,也就是房子建立好以后,在我的房子中如何能安装你想要的门窗呢?

在写程序的时候无法知道要被调用的类名,所以,在程序中无法直接 new 某个类的实例对象,而要用反射方式来做

 

以下是代码 用于了解反射机制

一共有三个类和一个配置文件

Point.class    ClassTest.java    ReflectTest.java    Config.properties

ReflectTest 类中讲解反射关于实现框架功能使用到了 Config.properties 配置文件

 

 

 

Config.properties

className= java.util.ArrayList  

 

---------------------------------------------------------

package Java.Lesson.Reflection;

class Point {

    // 一个 JavaBean 类,两个学习反射的例子类中会用到

   

    static {

       System. out .println( "Loading Class Point" );

    }

 

    private String name ;

    private String sex ;

    private int age ;

 

    Point(String n, String sex, int a) {

       this . name = n;

       this . sex = sex;

       this . age = a;

    }

 

    Point() {

       ;

    }

    @Override

    /* 一种注释语句,意思是 下边的这个方法必须是覆盖超类的方法,如果超类找不到这个方法,就会报错

    例如 public String toString(){} */

    public String toString() {

       return "name=" + this . name + ",sex=" + this . sex + ",age=" + this . age ;

    }

    public int getAge() {

       return age ;

    }

    public void setAge( int age) {

       this . age = age;

    }

    public String getName() {

       return name ;

    }

    public void setName(String name) {

       this . name = name;

    }

    public String getSex() {

       return sex ;

    }

    public void setSex(String sex) {

       this . sex = sex;

    }

    private void test() {

 

    }

    static void test2() {

      

    }

    public void MethodName() {

       System. out .println( " 运行方法成功 " );

    }

 

    @Override

    public int hashCode() {

       final int PRIME = 31;

       int result = 1;

       result = PRIME * result + age ;

       result = PRIME * result + (( name == null ) ? 0 : name .hashCode());

       result = PRIME * result + (( sex == null ) ? 0 : sex .hashCode());

       return result;

    }

 

    @Override

    public boolean equals(Object obj) {

       if ( this == obj)

           return true ;

       if (obj == null )

           return false ;

       if (getClass() != obj.getClass())

           return false ;

       final Point other = (Point) obj;

       if ( age != other. age )

           return false ;

       if ( name == null ) {

           if (other. name != null )

              return false ;

       } else if (! name .equals(other. name ))

           return false ;

       if ( sex == null ) {

           if (other. sex != null )

              return false ;

       } else if (! sex .equals(other. sex ))

           return false ;

       return true ;

    }

}

 

 

---------------------------------------------------------

package Java.Lesson.Reflection;

 

import java.lang.annotation.Annotation;

import java .lang.reflect.*;

 

class ClassTest {

    // 阐述反射机制 获取类的构造器,方法,成员变量,以及如何用反射调用方法的相关内容

 

    static Object create(Class clazz) throws Exception { // 方法的参数是一个类

       Constructor con = clazz.getDeclaredConstructor(String. class ,String. class , int . class );

       // 返回一个构造器,并指定了构造器对应的参数

       Object obj = con.newInstance( "Shiki" , "female" , 18);

       // 构建一个 con 中存放的构造器的实例传给 obj 对象

       return obj;

    }

 

    static void invoke1(Class clazz) {

       Method[] ms = clazz.getDeclaredMethods();

       // 返回类中的当前的所有方法,包括静态和私有方法 , 不包括接口实现的方法以及超类继承的方法

       // Method[] ms=obj.getClass().getMethods();

       // 返回类中的所有方法,包括所有公共的方法:接口实现的方法以及超类继承的方法,但不包括私有和静态方法

       /*

         * for(Method m:ms){ // 两种循环方法都可以 System.out.println(m); }

         */

       for ( int i = 0; i < ms. length ; i++) {

           System. out .println(ms[i]);

       }

    }

 

    static void invoke2(Class clazz, String MethodName) throws Exception {

       Method[] ms = clazz.getMethods();

       // 返回类中的当前的所有方法,包括静态和私有方法 , 不包括接口实现的方法以及超类继承的方法

       // Method[] ms=obj.getClass().getMethods();

       // 返回类中的所有方法,包括所有公共的方法:接口实现的方法以及超类继承的方法,但不包括私有和静态方法

       Object obj=create (clazz);

       for (Method m : ms) {

           if (MethodName.equals(m.getName()));

           m.invoke(obj, null );

       }

    }

 

    static void getField(Class clazz) {

       Field[] field = clazz.getDeclaredFields(); 

       // 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段

       for (Field f : field) {

           System. out .println(f);

       }

    }

 

    public static void main(String args[]) {

 

       try {

           System. out .println( "------------- Class.forName 方式获取类的构造方法 -------------" );

           Class c = Class.forName ( "Java.Lesson.Reflection.Point" ); // 加载 Point

           // 返回与带有给定字符串名的类或接口相关联的 Class 对象

           Constructor[] cons = c.getDeclaredConstructors();

           // 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法

           for ( int i = 0; i < cons. length ; i++) {

              System. out .println(cons[i]); // 通过循环,将所有构造方法的名称输出

           }

           System. out .println( "------------- Class.forName 方式获取类的方法 -----------------" );

           Method[] ms = c.getDeclaredMethods();

           /*

             * 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法

             * 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

             */

           for ( int i = 0; i < ms. length ; i++) {

              System. out .println(ms[i]); // 通过循环,将所有方法的名称输出

           }

           System. out .println( "----------- .class 方式获取类的单个构造器及成员变量的值 ----------" );

           Class clazz = Point. class ; // Point.class 获取 Point 类的类型,传递给 clazz

           Object obj = create (clazz); // clazz 作为参数,使用定义的 create 方法来创建一个类的实例

           System. out .println(obj);

           invoke1 (clazz); // 参数在之前已经使用过,保存了 Point 类的一个实例

           /*System.out.println(" 在获取所有方法中,调用某一个方法 ");

           invoke2(clazz, "MethodName");*/

          

           System. out .println( "------------ 利用反射机制,直接获取一个方法名,调用方法 ------------" );

           Method m=obj.getClass().getMethod( "MethodName" , null );

           m.invoke(obj, null );

           System. out .println( " 类的成员变量 " );

           getField (clazz);

          

           Annotation[] an=clazz.getAnnotations();  // 返回此元素上存在的所有注释

          

 

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       } catch (Exception e) {

       }

    }

}

 

 

 

---------------------------------------------------------

package Java.Lesson.Reflection;

 

import java.io.*;

import java.util.*;

public class ReflectTest {

    // 阐述反射机制 通过配置文件 获取类信息的相关内容

   

    public void PrintSize(){

      

    }

    public static void main(String args[]) throws Exception{

      

       Point p1= new Point( " 两仪式 " , " " ,18);

       Point p2= new Point( " 黑桐干也 " , " " ,18);

       Point p3= new Point( " 苍琦橙子 " , " " ,23);

       Point p4= new Point( " 黑桐鲜花 " , " " ,16);

       Point p5= new Point( " 黑桐鲜花 " , " " ,16);

       // ArrayList 中放入几个元素, size 就是几个   HashSet 不允许有重复的元素

      

      

       /*System.out.println("------------------1. 通过 main 的命令行参数来获取类型信息 -------------");

       // 命令行参数我定义的为 HashSet

       Class clazz=Class.forName(args[0]); 

       // 通过命令行参数的第一个值 args[0] 来获取输入的信息,加载参数中的类型信息给 clazz  */

      

      

      

       /*System.out.println("-----------2. 通过输入流读取 properties 配置文件来获取类型信息 ---------");

       // 在配置文件中我定义的类型为 ArrayList

       InputStream in=new FileInputStream("E:/J2EE/Java Workspace/Test/Java/Lesson/Reflection/Config.properties");

       // 用输入流来读 Config.properties 配置文件中的类型信息

       Properties p=new Properties();// 创建 Properties 类对象

       p.load(in);// 加载流中的内容

       Class clazz=Class.forName(p.getProperty("className"));

       // 利用 Class.forName 加载配置文件中的类型信息 赋值给一个类   */

      

      

      

       System. out .println( "--------------3. 通过类加载器读取配置文件来获取类型信息 ----------------" );

       // 在配置文件中我定义的类型为 ArrayList

       InputStream in=ReflectTest. class .getClassLoader().getResourceAsStream( "Java/Lesson/Reflection/Config.properties" );

       Properties p= new Properties();

       p.load (in);

       Class clazz=Class.forName (p.getProperty( "className" ));

       Collection coll=(Collection)clazz.newInstance ();

       // 实例化 clazz 中加载的类的一个对象,因为 clazz 中包含的是 HashSet ,就相当于 Collection coll=new HashSet()*/      

       coll.add(p1);  // Collection 集合中增加 Point 类型的对象

       coll.add(p2);

       coll.add(p3);

       coll.add(p4);

       coll.add(p5);

      

       System. out .println(coll.size());  //size 方法返回此 collection 中的元素数

    }

}

 

----------------------------------------------------- 内省 -----------------------------------------------------

1. 学习内省,需要先了解 javaBean

如果要在两个模块之间传递多个信息,可以将这些信息封装到一个类中,这种类的实例对象通常称之为值对象 (Value Object ,简称 VO) 。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问 ( 访问器,构造器 )

JavaBean 是一种特殊的 Java 类,主要用于传递数据信息,这种 Java 类的方法主要用于访问私有的字段,切方法名符合某种命名规则。 JavaBean 的属性是根据其中的 setter getter 方法来确定的,而不是根据其中的成员变量。如果方法名为 setId ,中文意思即为设置 Id ,如果方法名为 getId ,中文意思即为获取 Id ,去掉 set get 前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母也改成小写

外部类要访问被当作 JavaBean 的类的时候,它根本看不到 JavaBean 内部定义的成员变量, Javabean 的属性是根据方法名推断出来的

 

2. 一个符合 JavaBean 特点的类可以当作普通类一样进行使用,但把它当 JavaBean 用肯定会带来一些额外的好处,我们才会去了解和应用 JavaBean

JDK 中提供了对 JavaBean 进行操作的一些 API ,这套 API 就称为内省。如果要你自己去通过 getX 方法来访问私有的成员变量,就会有一定的难度,用内省这套 API 操作 JavaBean 比用普通类的方式更方便

Java EE 开发中,经常要使用到 JavaBean ,很多环境要求用 JavaBean 方式进行操作

3. Beanutils 工具包

       可以轻松实现内省的功能,需要加入两个 jar 包, beanutils.jar logging.jar ,在使用时候需要用 import 语句来导入包

import org.apache.commons.beanutils.BeanUtils;

import org.apache.commons.beanutils.PropertyUtils;

import org.apache.commons.logging.*;

       PropertyUtils 类进行 get set 方法, get 属性返回的结果为该属性本来的类型, set 属性只接收该属性本来的类型,

 

 

以下是代码,用于了解内省的部分机制

一共有两个类   IntrospectorTest.java    UserBean.java

 

package Java.Lesson.Introspector;

 

public class UserBean {

 

    private String name ;

    private String sex ;

    private int age ;

    public UserBean(){

      

    }

    public UserBean(String name, String sex, int age) {

       super ();

       this . name = name;

       this . sex = sex;

       this . age = age;

    }

    public int getAge() {

       return age ;

    }

    public void setAge( int age) {

       this . age = age;

    }

    public String getName() {

       return name ;

    }

    public void setName(String name) {

       this . name = name;

    }

    public String getSex() {

       return sex ;

    }

    public void setSex(String sex) {

       this . sex = sex;

    }

   

   

}

 

 

 

----------------------------------------------------

package Java.Lesson.Introspector;

 

import java.beans.BeanInfo;

import java.beans.IntrospectionException;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import org.apache.commons.*;

import org.apache.commons.beanutils.BeanUtils;

import org.apache.commons.beanutils.PropertyUtils;

import org.apache.commons.logging.*;

 

public class IntrospectorTest {

   

    // 构造 JavaBean 类对象,通过内省的方式,透过 JavaBean 修改某个对象的值 , 也就是利用 java 类实现了 javabean 的功能

    private static void setObjectName(UserBean user,String name,String value) throws Exception {

      

       // 第一种方法 构造一个 javabean ,获取属性数组,通过 if 判断,调用符合条件的方法  

       // 为了让两种方法可以同时使用,所以,第一种方法为写方法,第二种方法为读方法

       BeanInfo bean = Introspector.getBeanInfo (UserBean. class );

         // Introspector.getBeanInfo 方法把一个类当作 JavaBean ,用 BeanInfo 的对象来接收

         //Introspector 将分别分析 bean 的类和超类,寻找显式和隐式信息 , 使用这些信息构建一个全面描述目标 bean BeanInfo 对象

       PropertyDescriptor[] props = bean.getPropertyDescriptors();

       //PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性

       //getPropertyDescriptors 表示获得 beans PropertyDescriptor ,也就是返回 bean 的属性 ( 成员变量 )

       // 将返回的属性传递给受此 bean 支持的可编辑属性的 PropertyDescriptor 数组

       for (PropertyDescriptor prop : props) { // 通过循环获得 PropertyDescriptor 数组中的每个属性

           if (prop.getName().equals(name)) {

              // 获取属性数组中的名字,判断是否与要设置的属性名称相同

              Method setMethod = prop.getWriteMethod();

              // 属性数组中,调用 name 的属性值的写方法 传递给 Method 对象

              setMethod.invoke(user,value);

              // 通过 Method 对象调用 invoke 方法,实现调用 name 中包含的属性名称的 setter 方法的功能,第一个

              // 参数为 user ,是 javabean 的对象,第二个参数 value ,表示 setName 方法的参数,也就是要修改的值

              break ; // 如果判断的条件中有符合的条件,执行后立即退出

           }

       }

    }

    //eclipse 环境中 单击右键 --Refactor--extract method ,提取代码成为一个方法,简称为重构

   

    private static Object gettObjectName(UserBean user,String name) throws Exception {

      

       // 第二种方法 直接实例化一个 PropertyDescriptor 对象,通过参数指定要访问的属性名称,调用符合条件的方法

       return new PropertyDescriptor(name,user.getClass()).getReadMethod().invoke(user, null );

      

       // 以上是简便的写法,也可以类似于方法一中的格式   简便格式也可以用于 set 方法

       /*PropertyDescriptor props=new PropertyDescriptor(name,user.getClass());

         Method getMethod=props.getReadMethod();

         return getMethod.invoke(user,null);  */

    }

   

   

    public static void main(String[] args) throws Exception{

        IntrospectorTest demo = new IntrospectorTest();

        UserBean user= new UserBean();     

 

        setObjectName (user, "name" , " 两仪式 " ); // 方法一的设置属性的方法

        System. out .println(gettObjectName (user, "name" )); // 方法二的获取属性的方法

       

        BeanUtils.setProperty (user, "age" , "18" );

        // 使用 beanutils 插件的 BeanUtils 类的设置属性和获取属性方法

        System. out .println(BeanUtils.getProperty (user, "age" ));

       

        PropertyUtils.setProperty (user, "age" , 19);

        // 使用 beanutils 插件的 PropertyUtils 类的设置属性和获取属性方法

        System. out .println(PropertyUtils.getProperty (user, "age" ));

       

        // 还可以这样写

        //Integer i1=(Integer)BeanUtils.getProperty(user, "age");

        /* 虽然是获取 int 类型的 age ,但是 BeanUtils 可以在 set 的时候用字符串代替数字,从而读取出来的数字类型就是

           字符串类型,错误信息为: Cannot cast from String to Integer

         */

        Integer i2=(Integer)PropertyUtils.getProperty (user, "age" );

        // 获取 int 类型的 age ,还可以赋给一个 Integer 对象

        /*

         PropertyUtils get set JavaBean 的属性是什么类型,它的参数就是什么类型

         BeanUtils get set  JavaBean 的属性无论是什么类型,它的参数都是字符串类型,因为如果做 web 开发,

          从网页上的文本框等方式中获取的信息都是字符串,所以 BeanUtils 的这种参数为字符串类型的特性在 web 开发中

          使用 JavaBean 模型会更加简易   */

    }

}

----------------------------------------------------- 注解 -----------------------------------------------------

注解相当于一种标记,加了注解就等于打上了某种标记

avac 编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事,标记可以加在包,类,字段,方法,方法的参数以及局部变量上

java.lang 包中,可看到 JDK 中提供的最基本的 annotation ,以下是常见的三个注释语句

1. System.runFinalizersOnExit(true) 这个方法由于 JDK 的版本问题已经过时了,编译器会告诉使用者不建议使用这个方法,但是我还想继续使用它,所以我需要使用一种注解

@SuppressWarnings("Deprecation") --------- 告诉编译器对于过时或因其它问题造成的错误方法不发出警告

指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告,如果注释一个类来取消显示某个警告,同时注释一个方法来取消显示另一个警告,那么将在此方法中同时取消显示这两个警告。应该始终在最里层的嵌套元素上使用此注释,在那里使用才有效。如果要在特定的方法中取消显示某个警告,则应该注释该方法而不是注释它的类

2. @Deprecated--------- 表示下边的方法已经过时,建议不要使用

3. @override------------ 表示对一个方法声明打算重写超类中的另一个方法所进行的声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息,例如对这个方法进行 @override 注释, equals 方法与父类的 equals 方法有稍微的不同,就会默认为一种新方法,而不是从重写超类的方法,结果就会产生差异,这种小错误一般很难识别

 

 

自定义注解及其应用

定义一个最简单的注解: pbulic @interface MyAnnotation{}

把它加在某个类上, @MyAnnotation public class AnnotationTest{}

用反射进行测试 ReflectPoint 类的定义上是否有 @MyAnnotation

根据反射测试的问题,引出 @Retention 元注解的讲解,其三种取值, RetentionPolicy.SOURCE RetentionPolicy.CLASS RetentionPolicy.RUNTIME

RetentionPolicy.CLASS ------------ 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释

RetentionPolicy.RUNTIME-------------- 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。

RetentionPolicy.SOURCE--------------- 编译器要丢弃的注释

思考: @override,@SuppressWarnings @Deprecated 这三个注解的属性值分别是什么

演示和讲解 @target 元注解

Target 的默认值为任何元素,表示定义的注解可以对何种类型的声明进行注释,除了定义的,其他的不可以对其进行注解,设置 Target 等于 ElementType.METHOD ,原来加在类上的注解就报错了,应改为用数组方式设置 {Elementype METHOD ElementType Type}

 

 

为注解增加基本属性

一个注解相当于一个胸牌,如果你胸前贴了胸牌,你就是这个公司的员工,否则,就不是。如果还想区分出来是哪个部门的员工,这时候可以为胸牌在增加一个属性来进行区分。加了属性的标记效果为: @MyAnnotation(color=”red”)

 

定义基本类型的属性和应用属性

在注解类中增加 String color();

@MyAnnotation(color=”red”)

 

用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法

MyAnnotation a=MyAnnotationTest.class.getAnnotation(MyAnnotation.class)

System.out.println(a.color());

 

为属性指定缺省值

String color() default “yellow”

 

Value 属性

如果注解中有一个名称为 value 的属性,且你只想设置 value 属性,即其他属性都采用默认值或者你只有 value 这一个属性,那么可以省略 ”value=” 这部分,例如:

@MyAnnotation(“abc”)

 

数组类型的属性

int[] arrayAttr() default{1,2,3}

@MyAnnotation(arrayAtr={2,3,4})

如果数组属性中只有一个元素,这时候属性值部分可以省略大括号

 

枚举类型的属性

EnumTest.WeekDay enumAttr();

@MyAnnotation(enumAttr=EnumTest.WeekDay.FRI)

 

注解类型的属性

MetaAnnotation annotationAttr() default @MetaAnnotation(“xxx”)

@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”))

可以认为上边这个 @MyAnnotation MyAnnotation 类的一个实例对象,同样道理

可以认为上边这个 @MetaAnnotation MetaAnnotation 类的一个实例对象,

 

 

以下是代码,用于了解注解的部分功能

一共有三个类, AnnotationTest.java     MyAnnotation.java     MyAnnotationTest.java

 

 

package Java.Lesson.Annotation;

 

public class AnnotationTest {

   

    @Deprecated // 表示下边的方法已经过时,建议不要使用

   

    public void sayHello(){ // 这个方法没有用途了,我不想使用它了,我要用其他的方法来代替它,所以就要注释掉它

       System. out .println( "Hello World" );

    }

    @SuppressWarnings ( "deprecation" // 告诉编译器对于过时或因其它问题造成的错误方法不发出警告

   

    public static void main(String args[]){  

       System.runFinalizersOnExit( true );

       /* 已过时。 该方法具有固有的不安全性。它可能对正在使用的对象调用终结方法,

       而其他线程同时正在操作这些对象,从而导致不正确的行为或死锁。 */

      

       AnnotationTest at= new AnnotationTest();

       at.sayHello();

    }

   

    /*@override 表示对一个方法声明打算重写超类中的另一个方法所进行的声明。

      如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息  

      构造一个 javabean 类,实现其 equals hashcode 方法,透过反射机制用 HashSet 加载 javabean 类的对象,

      由于 HashSet 会自动由 equals hashcode 的默认排序实现方法对 javabean 的对象内容进行排序,

      所以由于 equals hashcode 方法名拼写错误或参数的类型以及返回值内容与父类的方法不同,

      会导致调用集合的 size 方法时,插入 HashSet 中的元素个数不等

      故需要使用 @override 注释来判断是否重写了超类中的另一个方法

      代码请见 Java.Lesson.Reflection  反射中已经阐述了这一种情况 */

}

 

 

------------------------------------------------------------------------

package Java.Lesson.Annotation;

 

import java.lang.annotation.*;

 

// 自定义注解及其应用   在其他类中创建一个 @MyAnnotation 的注解,就相当于创建了这个类的一个实例对象

 

@Retention (RetentionPolicy. RUNTIME )

/* 创建一个注解的生命周期,指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS

  只有元注释类型直接用于注释时, Target 元注释才有效。如果元注释类型用作另一种注释类型的成员,则无效   */

 

@Target (ElementType. TYPE /* 表示定义的 MyAnnotation 这个注解可以对类、接口(包括注释类型)或

                         枚举声明进行注释,但其他的不可以,例如:构造器和方法等   */

//  @Target(ElementType.METHOD)  // 表示可以对方法进行注释,其他的不可以

// 如果想对类,方法等多个都允许注释,可以通过 ", 逗号 " 来添加多个参数 例如:

//@Target((ElementType.METHOD,ElementType.TYPE))

public @interface MyAnnotation {

    String color() default "red" // 为注解增加一个基本的属性

    String name() default " 两仪式 " // 还可以为注解的属性设置默认值

    String value();

}

 

 

 

--------------------------------------------------------------------------------

package Java.Lesson.Annotation;

 

@MyAnnotation ( "value" // 自定义的注释 对类进行了注释 创建了一个 MyAnnotation 类的一个实例对象

// 如果注解有属性的话,就相当于类的一个构造器一样,构造的时候也要设置这个属性

// 注解还有一个特殊的属性叫做 value ,可以在设置值的时候可以用缺省的方式来设置值,而不用 valu="value"

public class MyAnnotationTest {

 

    public static void main(String args[]){

      

       if (MyAnnotationTest. class .isAnnotationPresent( MyAnnotation . class )){

           /* 通过反射机制,调用 isAnnotationPresent 方法,判断是否有指定类型的注释存在于此元素上,

           如果有返回 true ,否则返回 false  */

           System. out .println(MyAnnotationTest. class .getAnnotation( MyAnnotation . class ));

           /*getAnnotation 方法表示如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null

             判断 MyAnnotationTest 类中是否存在 MyAnnotation 类型的注释   */

       }

       /* 单纯构造一个注解类,然后判断此类是否包含了注解,获得返回结果打印 boolean 值,但不会有任何返回结果,

       需要在 MyAnnotation 自定义的注释类型中加入 @Retention 元注解   */

      

       MyAnnotation m=MyAnnotationTest. class .getAnnotation( MyAnnotation . class );

       System. out .println(m.color());  // 通过反射机制来获取注解属性的值

       System. out .println(m.name());

       System. out .println(m.value());

    }

}

 

 

 

----------------------------------------------------- 泛型 -----------------------------------------------------

JDK 1.5 的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据

泛型是提供给 javac 编译器使用的,编译器编译带类型说明的集合时会去除掉 类型 信息,使程序运行效率不受影响,所以加了类型说明以后, getClass() 方法的返回值还是和以前完全一样

 

类型转换不是要改变对象的本质,因为编译器看不清楚它是什么,我知道它是什么,我就告诉编译器它是一个什么东西

 

了解泛型

ArrayList<E> 类定义和 ArrayList<Integer> 类引用中涉及如下术语

ArrayList<E> 中的 E 称为类型变量

ArrayList<Integer> 中的 Integer 称为类型参数

ArrayList<Integer> 中的 <> 读作 typeof

整个 ArrayList<Integer> 称为参数化的类型

ArrayList 称为原始类型

 

参数化类型与原始类型的兼容性

参数化类型可以引用一个原始类型的对象,编译报告警告,例如:

Collection<String> c=new Vector();

原始类型可以引用一个参数化类型的对象,编译报告警告,例如:

Collection c=new Vector<String>();

 

参数化类型不考虑类型参数的继承关系

Vector<Object> v1=new Vector<String>();  // 错误

Vector<String> v2=new Vector<Object>();.  // 错误

 

思考题:下面的代码会报错误吗?

Vector v3=new Vector <String>();

Vector <Object> v4=v3;     这种转换是可以的

使用 ? 通配符可以引用其他各种参数化的类型, ? 通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法

 

向下限定通配符

正确: Vector<? extends Number> x=new Vector<Integer>();

错误: Vector<? extends Number> x=new Vector<String>();

 

向上限定通配符

正确: Vector<? super Integer> x=new Vector<Number>();

错误: Vector<? Super Integer> x=new Vector<Byte>();

 

无论是向上限定,还是向下限定,都包括自己本身

 

在网页中, 也可以 使用 泛型对 Map 集合的类型进行设置

<&

HashMap<String,Integer> hm= new HashMap<String, Integer>();

hm.put( " 两仪式 " ,18);

hm.put( " 黑桐干也 " , 18);

request.setAttribute(“map”,hm);

&>

<c:forEach items=”${map}” var=”entry”>

       ${entry.key}:${entry.value}

</c:forEach>

 

C++ 的模板函数引入自定义泛型

int add(int x,int y){

       return x+y;

}

float add(float x,float y){

       return x+y;

}

double add(double x,double y){

       return x+y;

}

函数的结构很相似,仅仅类型不同

C++ 使用模板函数,只写一个通用的方法,它可以适应各种类型,例如:

<T>T add(T x,T y){

       return (T)(x+y);

}

 

Java 的泛型方法没有 C++ 模板函数功能强大, java 中的如下代码无法通过编译:

<T>T add(T x,T y){

       return (T)(x+y);

}

泛型类似于 C++ 中的模板,大师这种相似性仅限于表面, Java 语言中的泛型基本上完全在编译器中实现,由编译器执行类类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除 (erasure) ,(编译器使用泛型类型信息保证类型安全,然后再生成字节码之前将其擦除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,因为这会为 Java 厂商升级其 JVM 造成难以逾越的障碍,因此采用了可以完全在编译器中实现的擦除方法

 

构造一个方法,通过泛型进行类型转换

public static <T> T convertType(Object obj){

       return <T>obj;

}

 

 

 

以下是代码,用于了解泛型机制的部分功能

只有一个类   GenericTest.java

package Java.Lesson.Generic;

 

import java.lang.reflect.*;

import java.util.*;

 

import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry.Entry;

public class GenericTest {

 

    public static void printCollectionObject(Collection<Object> coll){

       for (Object obj:coll){

           System. out .println(obj);

       }

       coll.add( "String" ); 

// 不会报错,因为编译器明确的知道这个集合里边要装入 Object 类型的元素, Object 是所有类型的超类,所以可以放入 String

    }

    public static void printCollectionAll(Collection<?> coll){

       for (Object obj:coll){

           System. out .println(obj);

       }

       //coll.add("String"); 

       /* 编译器只知道用 ? 通配符来接收各种类型的集合,但是不知道集合内部到底要放入什么类型的元素

         是要放入 String 类型,还是要放入 Date 类型?所以编译器由于无法判断集合到底要匹配什么类型,就会报错

         只能去接收任意类型的信息,而不能装入

         */

    }

    /*public static <T> T convertType(Object obj){

       return <T>obj;

    }*/  

    // 通过一个方法使用泛型,对传递进来的对象参数进行类型转换,我现在用的 JDK1.5 ,但是编译器不能识别 T 类型

    public static void main(String args[]) throws Exception {

       // 定义一个带有泛型的集合类,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据、

       Collection<Integer> coll= new ArrayList();

       // 尖括号内代表了要装入集合中的类型,设置 Integer 以后, long flaost String 等就都不能装入这个集合中了

       coll.add(1);

       /*coll.add(1L);

       coll.add(1F);

       coll.add("abc");*/

      

       ArrayList a1= new ArrayList();

       /* 后边要用到 get 方法, Collection 的对象没有 get 方法,所以用 ArrayList

         Collection 是没有顺序的方法,所以它的集合内容没有下标

         ArrayList 是按照数组形式存放的,所以可以根据下标取值

         */

       a1.add(1);

       a1.add(1L);

       a1.add( "abc" );

       // 装入 ArrayList 中的值相当于一个数组,所以下标是从 0 开始的

       Integer i1=(Integer) a1.get(0);

       /* 获取数组下标为 0 的那个值,也就是 al.add(1) 要获取 1 这个值, 1 原本是 int 类型,通过转换类型,

       Integer 的对象来接收,类型转换不是要改变对象的本质,因为编译器看不清楚它是什么,

       我知道它是什么,我就告诉编译器它是一个什么东西 */

       //Integer i2=(Integer) al.get(1); 

       // 运行这句代码会报错,因为 1L 本身是 long 类型,所以根本无法强制转换成为 int 类型

      

       ArrayList<Integer> a2= new ArrayList<Integer>();

       a2.add(1);

       Integer i2=a2.get(0);

       /* 这里没有通过强制类型转换就可以赋值,原因是在实例化 ArrayList 类的时候,就定义了泛型 --Integer 类型

         所以编译器已经知道了获取的内容是什么,而不用我们再告诉编译器了   */

      

       // 在运用反射的时候,也可以运用泛型来优化代码

       Class<String> clazz=String. class ;

       Constructor<String> constructor=clazz.getConstructor(StringBuffer. class );

       //getConstructor 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法

       String str1=constructor.newInstance( new StringBuffer( "abc" ));

       String str2=clazz.newInstance();

       System. out .println( "str1=" +str1);

       System. out .println( "str2=" +str2);

      

      

      

       /* 泛型是提供给 javac 编译器使用的,编译器编译带类型说明的集合时会去除掉 类型 信息,

       使程序运行效率不受影响,所以加了类型说明以后, getClass() 方法的返回值还是和以前完全一样 */

       System. out .println( new ArrayList<Integer>().getClass().getName());

       System. out .println( new ArrayList<String>().getClass().getName());

      

       // 参数化类型与原始类型的兼容性

       Collection<String> c1= new Vector();

       Collection c2= new Vector<String>();

       // 编译器不会产生错误,只是给了一个警告 参数化类型和原始类型的对象可以互相引用

      

       // 参数化类型不考虑类型参数的继承关系

       //Vector<Object> v1=new Vector<String>();  // 错误

       //Vector<String> v2=new Vector<Object>();  // 错误

       // 两个泛型的类型参数不能相互转换

 

       Vector v3= new Vector<String>();

       Vector<Object> v4=v3;

 

      

       // 某些情况要使用 ? 通配符引用其他各种参数化的类型

       Collection<Integer> c= new ArrayList<Integer>();

       //printCollectionObject(c);  // 会报错

       //c 本身是一个 Integer 类型,而方法接收的是 Object 类型,所以会报错

       printCollectionAll (c);

       // 方法的参数用一个 ? 通配符来接收各种内容的 Collection 集合

      

       // 向下限定通配符

       Vector<? extends Number> x1= new Vector<Integer>();

       // 错误: Vector<? extends Number> x=new Vector<String>();

       // 只能是 Number 类型的子类,以及它本身

 

       // 向上限定通配符

       Vector<? super Integer> x2= new Vector<Number>();

       // 错误: Vector<? Super Integer> x=new Vector<Byte>();

       // 只能是 Integer 类型的超类,以及它本身

 

       // 利用泛型可以限定 Map 集合的键值对的类型

       HashMap<String,Integer> hm= new HashMap<String, Integer>();

       hm.put( " 两仪式 " ,18);

       hm.put( " 黑桐干也 " , 18);

       // 如果不按照类型的格式顺序来写,也会报错

       //hm.put(18, 18);

      

       //HashMap entrySet() 方法返回一个 Map.Entry 类型的键值对的集合 , 对这个集合也可以通过泛型对类型进行设置

       Set<Map.Entry<String,Integer>> set=hm.entrySet();

       for (Map.Entry<String, Integer> map:set){

           System. out .println(map.getKey()+ ":" +map.getValue());

       }

      

      

      

    }

}

 

 

 

----------------------------------------------------- 类加载器 -----------------------------------------------------

类加载器是负责加载类的一个对象。 ClassLoader 类是一个抽象类

 

数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。

ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时, ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader" )本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

通常情况下, Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。

 

系统有多个类加载器,类加载器之间的父子关系,主要有三个类加载器

Bootstrap---------Bootstrap 不是 java 类,其他的都是 java 类,获取它时,会返回 null

ExtClassLoader

AppClassLoader----------- 默认的系统类加载器,第一个会先调用它

 

获取一个类的类加载器名称

.class .getClassLoader().getClass().getName()

 

每个类加载器负责加载特定位置的类

BootStrap----------- 爷爷级类加载器,负责加载 JDK/jre/lib/rt.jar 这个文件, rt.jar 这个文件中包含了 Java 需要使用的各种用 import 所导入的类,例如: util math

ExtClassLoader----------- 父亲级类加载器,负责加载 JDK/jre/lib/ext/*.jar 这个目录下的所有 jar 文件

AppClassLoader---------- 孩子级类加载器,负责加载 classpath 设置的所有 jar 文件和目录

 

加载一个类,先从底层的类加载器开始,从下向上搜索,有没有可以加载这个类的加载器,如果没有,在向上一级一级的搜索,当所有的祖宗类加载器没有加载到类,就回到类加载器的发起者,如果这个类加载器还加载不了,则抛出 ClassNotFoundException 异常,不是再去找发起者类加载器的儿子类,因为没有 getChild 方法,只有 getParent 方法, 返回委托的父类加载器,假设有 getChild 的方法的话,一个孩子只有一个父亲,而一个父亲可以有多个孩子,那么去找哪个孩子类呢?所以,设计上就不允许了

 

父亲级类加载器加载的类无法引用只能被子孙级类加载器加载的类

 

可以构建一个 MyServlet 文件,在 MyServlet get 请求上获取 Servlet 这个类的类加载器,此时获取的就不是 JVM 所提供的类加载器,而是 Tomcat 服务器所提供的类加载器,默认返回的是 WebAppClassLoader 类加载器

Servlet 文件打成 jar 文件,放入 JDK/jre/lib/ext 目录下,在运行这个 MyServlet 文件,会看到报错,因为 Servlet 这个类的 jar 文件是在 ExtClassLoader 类加载器的加载范围之内,所以肯定会去调用父亲级的类加载器,然后在 ExtClassLoader 类加载器的环境中所加载的类没有 HttpServlet 这个类文件,这个文件是运行 web 服务器端必须的文件,因为运行 MyServlet 环境的类已经完全由父亲级的 ExtClassLoader 类加载器来加载,所以应该将 HttpServlet 类的 jar 文件放入 ext 目录中,重启 tomcat ,这样就可以运行这个 Servlet 文件了( HttpServlet 类的 jar 文件位于 Tomcat/common/lib/servlet-api.jar ),返回的类加载器是 ExtClassLoader

 

 

以下是代码,用于了解类加载器的部分机制

只有一个类   ClassLoaderTest.java 

 

package Java.Lesson.ClassLoader;

 

public class ClassLoaderTest {

 

    public static void main(String args[]){

    System. out .println(ClassLoaderTest. class .getClassLoader().getClass().getName());

    //getClassLoader() 返回该类的类加载器,

    // 获取这个类的类加载器的类的名字

   

    System. out .println(System. class .getClassLoader());

    //System 类的加载器返回的是 null, 因为 System 是由类加载器 Bootstrap 加载的,它不是 java 类,其他的都是 java

   

    /* 鼠标右键单击当前类,选择 export 导出, Java--JAR file 导出为一个 jar 文件,选择当前的类,路径为:

      jdk1.5.0_04/jre/lib/ext 下,可自由命名,例如: ClassLoaderTest.jar 

      再次运行该程序会发现,这个类 ClassLoaderTest 的类加载器会由默认的 AppClassLoader 变成 ExtClassLoader*/

    }

}

 

 

 

----------------------------------------------------- 代理 -----------------------------------------------------

代理是为已存在的目标类的方法增加一些系统功能

我找一个代理买电脑和找厂商直接买电脑,主体业务效果是一样的吗,但有一些小的不同

静态代理类, JVM 可以在内存中动态的分配一个类,这就是动态代理类

 

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler 。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

 

代理类没有缺省的构造方法,想要实例化一个代理类,就必须构造一个有参的,代理类的构造方法的参数类型是 InvocationHandler

InvocationHandler 是代理实例的调用处理程序实现的接口

每个代码实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法

 

实现代理类的三个步骤

1.    创建字节码

2.    用字节码生成对象

3.    用代理类对象调用需要实现的方法

 

有了目标,也有了代理,代理仅仅是把待用请求给了目标,同时直接返回目标的结果,这与不用代理的效果一样,我们可以在代理中的 4 种可能的位置上添加系统功能代码

1.    在调用目标方法之前

2.    在调用目标方法之后

3.    在调用目标方法前后

4.    在处理目标方法异常的 catch 块中

如何把系统功能的代码加到 catch 块中的相应位置处呢?不能用字符串把一段代码传进去,可以把代码装到一个对象的某个方法里,然后把这个对象给你,你调用这个对象的该方法,即等于执行了我的代码

 

假设我有一个系统,刚开始要增加日志功能,以后运行一段时间后,又想去掉系统功能

所以 这样的系统就必须通过代理机制来管理系统的整个功能

 

----------------------------------------------------- 并发库 -----------------------------------------------------

 

JDK 1.5 使用 Executors 来创建线程

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值