javase(13)_java基础增强

一、Eclipse的使用

1、在eclipse下Java程序的编写和run as,debug as,及java运行环境的配置.
2、快捷键的配置,常用快捷键:
•内容提示:Alt + / •快速修复:Ctrl + 1 •导包:Ctrl + shift + O •格式化代码块:ctrl + shift + F •向前向后:Alt + 方向键 •添加注释 Ctrl+Shift+/ •除去注释 Ctrl+Shift+\
3、程序的调试和运行
•F5(跳入)、F6(跳过) 、F7(跳出) 、drop to frame(回撤到当前方法起始位置)、resume(跳到下一个断点 ) 、选中变量或表达式后watch可观察当前值.

二、Junit使用

1、@Before、@Test、@After基本使用
public class Demo4 {
    @Before
    public void before() {
        System.out.println("before");
    }
    @Test
    public void testRun() {
        Person p = new Person();
        p.run();
    }
    @Test
    public void testEat() {
        Person p = new Person();
        p.eat();
    }
    @After
    public void after() {
        System.out.println("after");
    }
}

运行结果:

before
eating.
after
before
runing.
after

2、@BeforeClass、@Test、@AfterClass基本使用

public class Demo5 {
    @BeforeClass
    public void beforeClass() {
        System.out.println("beforeclass");
    }
    @Test
    public void testRun() {
        Person p = new Person();
        p.run();
    }
    @Test
    public void testEat() {
        Person p = new Person();
        p.eat();
    }
    @AfterClass
    public void afterClass() {
        System.out.println("afterclass");
    }
}

运行结果:

before
eating.
runing.
after

三、JDK 5.0 新特性

JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加高效、清晰,安全的代码.

1、静态导入,(了解即可,一般没人这么用)

语法:•Import static 包名.类名.静态属性|静态方法|*
import static java.lang.System.out;
import static java.util.Arrays.sort;

public class Demo1 {
    public static void main(String[] args) {
        out.print("main");
        int[] a = new int[] { 6, 5, 3 };
        sort(a);
        for (int i : a)
            out.print(i);
    }
}

2、自动装箱/拆箱

•自动装箱:指开发人员可以把一个基本数据类型直接赋给对应的包装类.
•自动拆箱:指开发人员可以把一个包装类对象直接赋给对应的基本数据类型.
public class Demo2 {
    public static void main(String[] args) {
        Integer i = 1;// 装箱
        int j = i;// 拆箱
        // 典型应用,list里只能装对象
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);// 1被自动包装为Integer
        int k = list.get(0);// 自动拆箱
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            int m = it.next();// 拆箱
        }
    }
}

3、增强for循环

•引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需先获得数组的长度或集合的迭代器,比较麻烦!
•JDK5中定义了一种新的语法——增强for循环,以简化此类操作.增强for循环只能用在数组、或实现Iterable接口的集合类上,如list,set可以,map不行.

Map for增强实例:

package cn.itcast.demo;

import java.util.*;
import java.util.Map.Entry;
import org.junit.Test;

public class Demo3 {

    @Test
    public void test3(){
        Map map=new HashMap();
        map.put("1", "aaa");
        map.put("2", "bbb");
        map.put("3", "ccc");
        //传统方式1
        Set set=map.keySet();
        Iterator it=set.iterator();
        while(it.hasNext()){
            String key=(String)it.next();
            String value=(String) map.get(key);
            System.out.println("key="+key+",value="+value);
        }
        //传统方式2
        Set set2=map.entrySet();
        Iterator it2=set2.iterator();
        while(it2.hasNext()){
            Map.Entry  entry=(Entry)it2.next();
            System.out.println("key="+entry.getKey()+",value="+entry.getValue());
        }
        //增强for循环的1种方式,实际转为的set视图只存了key
        for(Object obj:map.keySet()){
            String key2=(String)obj;
            String value2=(String)map.get(key2);
            System.out.println("key2="+key2+",value2="+value2);
        }
        //增强for循环的2种方式
        for(Object obj:map.entrySet()){
            Map.Entry entry3=(Entry) obj;
            String key3=(String) entry3.getKey();
            String value3=(String) entry3.getValue();
            System.out.println("key3="+key3+",value3="+value3);
        }
        //增强for循环需要注意的问题:只适合取数据,不适合更改
        int arr[]={1,2,3};
        for(int i: arr){
            i=10;
        }
        System.out.println(arr[0]); // 1
        List li=new ArrayList();
        li.add("1");
        for(Object obj : li){
            obj="888";
        }
        System.out.println(li.get(0));// 1
    }
}

4、可变参数

语法:

public void foo(int … args){}//三个点
注意事项:
•调用可变参数的方法时, 编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数.
•可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个长度可变的参数.
public class Demo4 {
    @Test
    public void testSum(){
        sum(1,2,3,4);
        int arr[]={5,6,7};
        sum(arr);
    }
    public void sum(int ...nums){
        int sum=0;
        for(int i:nums){
            sum+=i;
        }
        System.out.println(sum);        
    }
    //可变参数注意的问题
    //public void aa(int ...nums,int s)//不可以,编译器晕逼了
    //public void bb(int s ,int ...nums)//可以
    @Test
    public void  asListTest(){//api: public static <T> List<T> asList(T... a)
        List list=Arrays.asList("1","2","3");
        System.out.println(list);//[1, 2, 3] 
        
        String arr[]={"1","2","3","4"};
        list=Arrays.asList(arr);
        System.out.println(list);//[1, 2, 3, 4] arr是对象数组
        
        int nums[]={1,2,3,4,5};
        list=Arrays.asList(nums);
        System.out.println(list);//[[I@120d62b],nums是一个对象
        
        Integer nums2[]={1,2,3,4,5};
        list=Arrays.asList(nums2);
        System.out.println(list);//[1, 2, 3, 4, 5]  
    }
}

5、枚举类

为什么需要枚举?
•一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,Java5以后可以直接使用枚举予以解决.新增的 enum 关键字用于定义一个枚举类.
枚举类具有如下特性:
•枚举类也是一种特殊形式的Java类.
•枚举类中声明的每一个枚举值代表枚举类的一个实例对象.
•与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的.
•枚举类也可以实现接口、或继承抽象类.
•JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型.

定义枚举的狗找函数、方法、字段:

/*
 * jdk5之前,只能这么定义
 * class Grade{ 
 *      private Grade(){ }
 *      public static final Grade A=new Grade();
 *      public static final Grade B=new Grade();
 *      public static final Grade C=new Grade(); 
 *      public static final Grade D=new Grade();
 *      public static final Grade E=new Grade(); 
 *  }
 */
enum Grade { 
    A("100-90"), B("89-80"), C("79-70"), D("69-60"), E("59-0");
    private String value;
    private Grade(String value){
        this.value=value;
    }
    public String getValue(){
        return this.value;
    }
}

public class Demo1 {
    @Test
    public void test() {
        print(Grade.B);
    }
    
    public void print(Grade g){ // A B C D E
        String value=g.getValue();
        System.out.println(value);
    }
}

带抽象方法的枚举:

enum Grade {// class A 100-90优 B 89-80良 C 79-70 一般D 69-60差 E 59-0不及格
    //必须实现抽象方法
    A("100-90"){
        public String localeValue(){
            return "优";
        }
    },
    B("89-80"){
        public String localeValue(){
            return "良";
        }
    },
    C("79-70"){
        public String localeValue(){
            return "一般";
        }
    };
    
    private String value;
    private Grade(String value){
        this.value=value;
    }
    public String getValue(){
        return this.value;
    }
    public abstract String localeValue();
}

public class Demo1 {
    @Test
    public void test() {
        print(Grade.B);    //89-80,良    
    }
    
    public void print(Grade g){ // A B C D E
        String value=g.getValue();
        String value2=g.localeValue();
        System.out.println(value+","+value2);
    }
}

注意:

Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法.常用方法:

•name() //返回此枚举常量的名称,有毛用
•ordinal() //返回枚举常量的序数
•values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便.

6、反射

1、java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法.简而言之,动态获取类中信息就是反射.

2、java反射应用在很多java软件和框架中,例Tomcat,提供了处理请求和应答的方式,因为具体的处理动作不同,所以对外提供了接口,由开发者来实现具体的请求和应答处理.这个接口就是 servlet,开发者只要实现servlet接口,并配置相应的配置文件,对Tomcat来说就是在web.xml中配置servletname也就是类名,这样tomcat就能通过所谓的反射来加载该类的class文件,并获取类中的所有信息,从而实现开发者自定义的请求和应答处理方式.

3、类用来描述对象共性的信息,用来描述字节码文件的共性的类是Class类,该类提供获取字节码文件中的内容,包括类名称,属性和函数.反射依靠该类来完成.

4、获取字节码文件对象的三种方式:

  1.Object类的getClass()方法,必须实例化类

  2.任何数据类型都具备一个class静态属性(Objcet类的属性)来获取对于的Class对象.

  3.通过给定的类的字符串名称就可以获取该类Class对象,更易扩展,该方法是forName(). 一般使用这种

  例:String name="cn.itcast.Person"; Class c = Class.forName(name);

5、反射构造函数

import java.lang.reflect.Constructor;

class Person{
    String name;
    int age;
    public Person(){
        System.out.println("wu can");
    }
    public Person(String name,int age){
        System.out.println("you can");
    }
}

public class TestPerson {
    public static void main(String[] args) throws Exception{
        Person p = new Person();
        //根据字符串的名称查找该类的字节码文件,加载进内存,创建该字节码文件对象,创建对应的Person对象.
        String name="com.czbk.faceObjcet.Person";
        Class clazz = Class.forName(name);
        //只能调用无参构造函数
        Person p1 =(Person)clazz.newInstance();  
        //调用有参构造函数,getDeclaredConstructor可获取私有构造函数
        Constructor c = clazz.getConstructor(String.class,int.class);
        Object o=c.newInstance("ww",26);
    }
}

6、反射字段

public class TestPerson {
    public static void main(String[] args) throws Exception{
        String str="com.czbk.faceObjcet.Person";
        Class clazz = Class.forName(str);
        Object o = clazz.newInstance();
        Field f = clazz.getField("name");  //只能获取公有属性,可获取父类
        Object obj = f.get(o);
        
        Field f1 = clazz.getDeclaredField("age");//可获取私有属性,只限本类
        f1.setAccessible(true); //对私有字段的访问取消权限检查,暴力访问
        System.out.println(f1.get(o));
        f1.setInt(o,22);
        System.out.println(o);
    }
}

7、反射方法

public class TestPerson {
    public static void main(String[] args) throws Exception{
        String str="com.czbk.faceObjcet.Person";
        Class clazz = Class.forName(str);
        Object o = clazz.newInstance();
        //调用无参方法
        Method method=clazz.getMethod("toString", null);
        method.invoke(o, null);
        //调用有参方法
        Method method1=clazz.getMethod("toString1", String.class);
        method1.invoke(o, "sss");
    }
}

8、主板运行实例(接口+配置)

public interface PCI {
    void run();
    void close();
}

public class SoundCard implements PCI{
    public void run() {
        System.out.println("SoundCard Run");
    }
    public void close() {
        System.out.println("SoundCard Close");
    }
}

public class NetCard implements PCI{
    public void run() {
        System.out.println("NetCard run");
    }
    public void close(){
        System.out.println("NettCard clase");
    }
}

public class MainBoard {
    /*public void run(PCI p){
        p.run();
    }
    public void clase(PCI p){
        p.close();
    }*/
    public static void main(String[] args) throws Exception {
        //这种方式每次增加新硬件时都需要修改主板源代码
        /*SoundCard sc = new SoundCard();
        MainBoard mb = new MainBoard();
        mb.run(sc);
        mb.clase(sc);*/
        
        //增加硬件无需修改MainBoard源代码,只要在配置文件中添加
        Properties pt = new Properties();
        FileInputStream is = new FileInputStream(new File("PCI.properties"));
        pt.load(is);
        
        for(int x=0;x<pt.size();x++){
            String PCIName=pt.getProperty("pci"+x);
            Class clazz = Class.forName(PCIName);
            PCI p=(PCI)clazz.newInstance();
            Method method = clazz.getMethod("run", null);
            method.invoke(p, null);
        }
    }
}

7、内省(Introspector)

1、为什么要学内省?
•开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性.
public class Demo1 {
    //得到bean所有属性
    @Test
    public void test1() throws IntrospectionException{
        BeanInfo info=Introspector.getBeanInfo(Person.class);
        //去掉Object里的属性(也就是class属性)
        BeanInfo info2=Introspector.getBeanInfo(Person.class,Object.class);
        PropertyDescriptor[] pds=info.getPropertyDescriptors();
        for(PropertyDescriptor pd:pds){
            System.out.println(pd.getName());
            //ab age class name  password
        }
    }
    //操纵bean的指定属性:age
    @Test
    public void test2() throws  Exception{
        Person p=new Person();
        PropertyDescriptor pd=new PropertyDescriptor("age", Person.class);
        //得到属性的写方法,为属性赋值
        Method method=pd.getWriteMethod();
        method.invoke(p, 45);
        System.out.println(p.getAge());//45
        
        //获取属性的值
        method=pd.getReadMethod();
        System.out.println(method.invoke(p, null));//45
    }
    //高级内容,获取当前操作的属性的类型
    @Test
    public void test3() throws  Exception{
        Person p=new Person();
        PropertyDescriptor pd=new PropertyDescriptor("age", Person.class);
            
        //得到属性的写方法,为属性赋值
        Method method=pd.getWriteMethod();
        System.out.println(pd.getPropertyType());//int
        method.invoke(p, 45);
        System.out.println(p.getAge());//45
        
        //获取属性的值
        method=pd.getReadMethod();
        System.out.println(method.invoke(p, null));//45
    }
}

2、内省—beanutils工具包

Sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtils
Beanutils工具包的常用类:•BeanUtils、•PropertyUtils、•ConvertUtils.regsiter(Converter convert, Class clazz)•自定义转换器
//使用beanUtils操纵bean的属性 ( 第三方)
public class Demo1 {
    @Test
    public void test1() throws Exception{
        Person p=new Person();
        BeanUtils.setProperty(p, "age", 456);
        System.out.println(p.getAge());//456
    }
    @Test
    public void test2() throws Exception{
        String name="aaaa";
        String age="123";
        String password="pw";
                
        Person p=new Person();
        //支持8种基本类型自动转换
        BeanUtils.setProperty(p, "name", name);
        BeanUtils.setProperty(p, "age", age);
        BeanUtils.setProperty(p, "password", password);
        
        System.out.println(p.getName());//aaaa
        System.out.println(p.getAge());//123
        System.out.println(p.getPassword());//pw

    }
    @Test
    public void test3() throws Exception{

        String birthday="1983-12-1";
        
        //为了让日期赋值到bean的birthday属性上,给beanUtils注册一个日期转换器
        //ConvertUtils.register(converter, clazz);
        ConvertUtils.register(new Converter(){
            
            public Object convert(Class type, Object value) {
                if(value==null) return null;
                if(!(value instanceof String)){
                    throw new ConversionException("只支持String类型的转换");
                }
                String str=(String)value;
                if(str.trim().equals("")) return null;
                SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd",Locale.US);
                try {
                    return df.parse(str);
                } catch (ParseException e) {
                    throw new RuntimeException(e);                    
                }
            }
        }, Date.class);
        Person p=new Person();
        BeanUtils.setProperty(p, "birthday", birthday);
        System.out.println(p.getBirthday());//pw
        System.out.println("___"+BeanUtils.getProperty(p, "birthday"));
    }
    public void test5() throws Exception {
        Map map=new HashMap();
        map.put("name", "aaa");
        map.put("password", "123");
        map.put("brithday", "1980-09-09");
        ConvertUtils.register(new DateLocaleConverter(), Date.class);
        Person p=new Person();
        //用map集合填充bean属性,map关键字和bean属性要一致
        BeanUtils.populate(p, map);
    }
}

8、泛型(Generic)

1、JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题,例:

ArrayList list = new ArrayList(); list.add("abc"); Integer num = (Integer) list.get(0);  //运行时会出错,但编码时发现不了
JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出).
2、注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据.但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为"擦除".
•使用泛形时,泛形类型须为引用类型,不能是基本数据类型
ArrayList<String> list = new ArrayList<Object>(); 错,左右必须完全一致 ArrayList<Object> list = new ArrayList<String>(); 错 ArrayList<String> list = new ArrayList (); 对,可以一边没有 ArrayList list = new ArrayList<String>(); 对

3、自定义泛形——泛型方法

Java程序中的普通方法、构造方法和静态方法中都可以使用泛型.方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写.<T>通常需放在方法的返回值声明之前.例如:
public static <T> void doxx(T t);
•编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素.
public class Demo1 {
    // 编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素.
    public <T> void reverse(T arr[]) {
        int startindex = 0;
        int endindex = arr.length - 1;
        while (true) {
            if (startindex >= endindex)
                break;
            T temp = arr[startindex];
            arr[startindex] = arr[endindex];
            arr[endindex] = temp;
            startindex++;
            endindex--;
        }

    }
}
注意:
•只有对象类型才能作为泛型方法的实际参数.
•在泛型中可以同时有多个类型,例如:
public static <K,V> V getValue(K key) { return map.get(key);}
4、自定义泛形——泛型类和反射泛形
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型),语法格式如下:
public class GenericDao<T> { private T field1; public void save(T obj) {} public T getId(int id) {} }
注意,静态方法不能使用类定义的泛形,而应单独定义泛形.
5、工厂中使用到泛型
//单例
public class ServiceFactory {
    
    private Properties serviceConfig = new Properties();
    //单例的构造函数也只执行一次
    private ServiceFactory(){
        InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("service.properties");
        try {
            serviceConfig.load(in);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private static ServiceFactory instance = new ServiceFactory();
    public static ServiceFactory getInstance(){
        return instance;
    }
    public <T> T createService(Class<T> clazz){
        //clazz.getName()拿到的带包名
        String name = clazz.getSimpleName();
        String className = serviceConfig.getProperty(name);
        try {
            T service = (T) Class.forName(className).newInstance();
            return service;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

6、泛型的高级应用——通配符 略

9、Annotation(注解)

1、什么是Annotation,以及注解的作用?三个基本的 Annotation:
•@Override: 限定重写父类方法, 该注解只能用于方法
•@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
•@SuppressWarnings: 抑制编译器警告.
2、Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行.在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类.最重要的重要就是直观,替代了配置文件.
3、掌握注解技术的要点:
•如何定义注解
•如何反射注解,并根据反射的注解信息,决定如何去运行类
4、自定义 Annotation
定义新的 Annotation 类型使用 @interface 关键字
声明注解的属性
•注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述.
•注解属性允许的类型only primitive type, String, Class, annotation, enumeration,一维数组are permitted(集合不允许)
•Annotation 的属性声明方式:String name();
•属性默认值声明方式:String name() default "xxx";
•特殊属性value:如果注解中只有一个名称value的属性,那么使用注解时可以省略value=部分,如@MyAnnotation("xxx")
5、JDK 的元 Annotation
元 Annotation指修饰Annotation的Annotation.JDK中定义了如下元Annotation:
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域.
•RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解. 这是默认值
•RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注释
•RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释
@Target:指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value,类型为ElementType的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解
@Target(value={ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
    
    String name() default "zxx";
    String password() default "123";
    int age() default 12;
    Gender gender() default Gender.男;//枚举
    MyAnnotation2 my2() default @MyAnnotation2(name="llll");
    Class clazz() default String.class;
    String[] ss() default {"aa","bbb"};
    int[] i() default {1,2};
}

6、提取 Annotation 信息

当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取,具体方法见api.
如下是一个注解可以实现的目的,在一个方法上加个注解,即可自动创建数据源,具体解析过程略.
public class CategoryDao {
    
    private ComboPooledDataSource combods;

    @Injectpublic void setCombods(ComboPooledDataSource combods) {
        this.combods = combods;
    }
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
    String driverClass() default "com.mysql.jdbc.Driver";
    String jdbcUrl() default "jdbc:mysql://localhost:3306/bookstore";
    String user() default "root";
    String password() default "root";
}

7、注解在servlet 3.0中的使用案例,替代了在web.xml中的配置:

@WebFilter(
        urlPatterns = { "/ServletDemo1" }, 
        initParams = { 
           @WebInitParam(name = "charset", value = "UTF-8", description = "编码")
        })
public class FilterDemo1 implements Filter {
    FilterConfig fConfig=null;
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        String charset = fConfig.getInitParameter("charset");
        HttpServletRequest req = (HttpServletRequest)request;
        req.setCharacterEncoding(charset);
        chain.doFilter(request, response);
    }

    public void init(FilterConfig fConfig) {
        this.fConfig = fConfig;
    }
    
    public void destroy() {
        System.out.println("FilterDemo1 destroy!");
    }
}

10、动态代理

代理对象存在的价值:主要用于拦截对真实业务对象的访问

1、jdbc数据库连接池中使用动态代理拦截close方法,将连接放入连接池而不是关闭:

public synchronized Connection getConnection() throws SQLException {
    
    if(list.size()<=0){
        throw new RuntimeException("数据库忙,请稍会再来!!");
    }
    Connection conn = list.removeFirst();   //list.get()不行
    return new MyConnectionHandler(conn,this).getWarpConn();
}

class MyConnectionHandler implements InvocationHandler {
    private Connection realConnection;
    private Connection warpedConnection;
    private MyDataSource dataSource;

    private int maxUseCount = 5;
    private int currentUserCount = 0;

    MyConnectionHandler(Connection conn,MyDataSource dataSource) {
        this.realConnection=conn;
        this.dataSource = dataSource;
    }

    Connection getWarpConn() {
        warpedConnection = (Connection) Proxy.newProxyInstance(this
                .getClass().getClassLoader(), new Class[] { Connection.class },this);
        return warpedConnection;
    }
   //proxy:把代理对象自身传递进来 method:代表当前调用的方法 args:调用方法的参数
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if ("close".equals(method.getName())) {
            currentUserCount++;
            if (currentUserCount < maxUseCount)
                dataSource.connectionsPool.addLast(warpedConnection);
            else {
                realConnection.close();
                dataSource.currentCount--;
            }
        }
        return method.invoke(realConnection, args);
    }
}

2、动态代理改写之前的解决全站乱码拦截器   *经典

public class CharacterEncodingFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        request.setCharacterEncoding("UTF-8"); // 解决post乱码

        chain.doFilter((ServletRequest) Proxy.newProxyInstance(
                CharacterEncodingFilter.class.getClassLoader(),
                request.getClass().getInterfaces(), new InvocationHandler() {

                    public Object invoke(Object proxy, Method method, Object[] args) {
                        if (!method.getName().equals("getParameter")) {
                            return method.invoke(request, args);
                        }
                        if (!request.getMethod().equalsIgnoreCase("get")) {
                            return method.invoke(request, args);
                        }

                        String value = (String) method.invoke(request, args);
                        if (value == null) {
                            return null;
                        }
                        return new String(value.getBytes("iso8859-1"), "UTF-8");
                    }

                }), response);
    }

    public void destroy() {
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

3、动态代理改写之前的压缩输出拦截器

public class GzipFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) resp;

        ResponseProxy proxy = new ResponseProxy(response);
        chain.doFilter(request, proxy.createProxy());

        byte[] out = proxy.getBuffer(); // 得到目标资源的输出

        System.out.println(new String(out, "UTF-8"));

    }

    class ResponseProxy {
        private ByteArrayOutputStream bout = new ByteArrayOutputStream();
        private PrintWriter pw = null;

        public byte[] getBuffer() {
            if (pw != null) {
                pw.close();
            }
            return bout.toByteArray();
        }

        private HttpServletResponse response;

        public ResponseProxy(HttpServletResponse response) {
            this.response = response;
        }

        public HttpServletResponse createProxy() {
            return (HttpServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(),
                response.getClass().getInterfaces(), new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args){
                        if (!method.getName().equals("getWriter")&&
                                !method.getName().equals("getOutputStream")) {
                            method.invoke(response, args);
                        }

                        if (method.getName().equals("getWriter")) {//PrintWriter.write("中国");
                            pw = new PrintWriter(new OutputStreamWriter(bout, "UTF-8"));
                            return pw;
                        }

                        if (method.getName().equals("getOutputStream")) { 
                            return new ServletOutputStream() {
                                @Override
                                public void write(int b) throws IOException {
                                    bout.write(b);
                                }
                            };
                        }
                        return null;
                    }
                });
        }

    }
    public void destroy() {
    }
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

4、动态代理+注解,实现权限管理

//service方法中,权限可精确到具体的方法
@Permission("添加分类")
public void addCategory(Category c){
    cdao.add(c);
}

@Permission("查看分类")
public Category findCategory(String id){
    return cdao.find(id);
}

public class ServiceFactory {
    
    private ServiceFactory(){}
    private static ServiceFactory instance = new ServiceFactory();
    public static ServiceFactory getInstance(){
        return instance;
    }
    
    public BusinessService createService(final User user){
        final BusinessService service = new BusinessServiceImpl();
        
        return (BusinessService) Proxy.newProxyInstance(ServiceFactory.class.getClassLoader(), 
                service.getClass().getInterfaces(), new InvocationHandler(){
            
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
            
                //得到web层调用的方法
                String methodName = method.getName();  //addCategory
                
                //反射出真实对象上相应的方法,检查真实对象方法上有没有权限注解
                Method realMethod = service.getClass().getMethod(methodName, 
                        method.getParameterTypes());
                Permission permission = realMethod.getAnnotation(Permission.class);
                if(permission==null){
                    return method.invoke(service, args);
                }
                
                //真实对象相应的方法上有权限注解,则得到访问该方法需要的权限
                Privilege p = new Privilege(permission.value());//得到方法需要的权限
                
                //检查用户是否有权限  //AppContext ThreadLocal
                //得到用户所有权限
                if(user==null){
                    throw new SecurityException("您没有登陆");
                }
                
                List<Privilege> list = service.getUserAllPrivilege(user);
                if(list.contains(p)){
                    return method.invoke(service, args);
                }
                throw new SecurityException("你没有权限");
            }
        });
    }
}

public class CategoryServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        String method = request.getParameter("method");
        if("add".equals(method)){
            add(request,response);
        }
        if("getAll".equals(method)){
            getAll(request,response);
        }
    }

    private void getAll(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        BusinessService service = ServiceFactory.getInstance().
                createService((User)request.getSession().getAttribute("user"));
        try{
            List list = service.getAllCategory(); 
            request.setAttribute("categories", list);
            request.getRequestDispatcher("/manager/listcategory.jsp").forward(request, response);
        }catch (Exception e) {
            if(e.getCause() instanceof SecurityException){
                request.setAttribute("message",  e.getCause().getMessage());
            }
        }
    }

    private void add(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        BusinessService service = ServiceFactory.getInstance().
                createService((User)request.getSession().getAttribute("user"));
        try {
            Category c = WebUtils.request2Bean(request, Category.class);
            c.setId(UUID.randomUUID().toString());
            service.addCategory(c);
            request.setAttribute("message", "添加成功");
        } catch (Exception e) {
            if(e.getCause() instanceof SecurityException){
                request.setAttribute("message", e.getCause().getMessage());
            }else{
                e.printStackTrace();
                request.setAttribute("message", "添加失败");
            }
        }
        request.getRequestDispatcher("/message.jsp").forward(request, response);
        
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

 

你可能感兴趣的:(javase(13)_java基础增强)