Java中的泛型、反射和正则

来源:互联网 发布:windows xp系统还原 编辑:程序博客网 时间:2024/05/18 03:10

一,泛型

1. 泛型定义:

  • java5开始出现的一种对Java语言类型的一种拓展,以支持创建可以按类型进行参数化的类;

    可以把类型参数看作是使用参数类型时指定的类型占位符,就好比方法的形式参数是实际参数的占位符一样。

2. 使用泛型的优势:

  • 类型安全,使编译器对泛型定义的类型做判断限制,如保证TreeSet里的元素类型必须一致;
  • 消除强制类型的转换,如使用Comparable比较时每次都需要类型强转;

3. 泛型定义在类和接口

定义在类
  • 定义:在类声明时通过一个标识符表示类中某个字段的类型或者某个方法的返回值或参数的类型,这样在类声明或实例化的时候只要指定自己需要的类型就ok。
  • 使用时机:当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。
  • 使用格式:


    class 类名<泛型类型1,泛型类型2……>{
    泛型类型 变量名;
    泛型类型 方法名(){}
    返回值类型方法名(泛型类型变量名){}
    }

  • 使用带泛型的类:

    类名<具体类> 对象名= new 类名<具体类>();

  • 代码示例:

    class worker {}class Utils<QQ> {    private QQ q;    public void setObject(QQ q) {        this.q=q;    }    public QQ getObject(){        return q;    }}class GenericDemo1 {    public static void main(String[] args) {        Utils<Worker> u=new Utils<Worker>();        u.setObject(new Worker);        Worker w=u.getObject();}
  • 类型参数规范:推荐使用规范,泛型只保存在源文件中,class文件中不存在;也就是说在编译阶段就会丢失,基本数据类型不能作为泛型类型;

    • K 键,比如映射的键key的类型
    • V 值,比如Map的值value类型
    • E 元素,比如Set Element表示元素,元素的类型
    • T 泛型,Type的意思
定义在接口
  • java5后,可以声明泛型接口,声明方式和声明泛型类是一样的。

    `public interface 接口名{}

  • 泛型接口子类有两种方式:

    • 直接在子类后申明泛型: public class DaoImpl<T> implements IDAO<T>{}
    • 在子类实现的接口中给出具体的泛型类型: public class DaoImpl implements IDAO<String>{}

4. 泛型定义在方法上

  • 概述:

    泛型类定义的泛型,在整个类中有效,如果被方法使用那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了;

    为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。

  • 格式:<泛型标签> 返回值类型方法名([泛型标签参数]...)

  • 特殊之处:

    • 静态方法不可以访问类上定义的泛型
    • 如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上
  • 代码示例:
    class Demo<T> {        //该方法类型跟随类        public void show(T t) {             System.out.println("show;"+t);        }        //方法可以接收不同的数据类型        public <Q> void print(Q q) {            System.out.println("print:"+q);        }        //将泛型定义在方法上,若只在形参中定义泛型,将会报错        public static <W> void method(W w) {              System.out.println("method:"+w);        }    }    class GenericDemo2 {         public static void main(String[] args) {            Demo <String> d = new Demo<String>();            //show方法只能接收字符串类型            d.show("haha");            //d.show(4);    //接收int类型将会报错            d.print(5);            d.print("hehe");            Demo.method(6);
  • 运行结果:

5. 声明多个泛型类型和通配符

声明多个泛型类型
  • 若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可
通配符
  • 使用 ? 来表示未知类型的泛型对象,也可以理解为占位符

6. 泛型的限定

上限:
  • 格式:?extends E:可以接收E的子类型。上限
下限:
  • 格式:?super E:可以接收E类型或者E的父类型
代码示例:
    import java.util.Comparator;    import java.util.Iterator;    import java.util.TreeSet;    public class GenericDemo {        public static void main(String[] args) {            TreeSet<Student> ts = new TreeSet<Student>(new Comp());            ts.add(new Student("Sabc03"));            ts.add(new Student("Sabc01"));            ts.add(new Student("Sabc02"));            printColl(ts);            System.out.println("------");            TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());            ts1.add(new Worker("Wabc02"));            ts1.add(new Worker("Wabc01"));            ts1.add(new Worker("Wabc03"));            printColl(ts1);        }        public static void printColl(TreeSet<? extends Person> ts) {    // 泛型上限定            Iterator<? extends Person> it = ts.iterator();            while (it.hasNext()) {                System.out.println(it.next().getName());            }        }    }    class Comp implements Comparator<Person> {  // Comparator的参数为?super E,此处为泛型下限定        public int compare(Person p1, Person p2) {            return p1.getName().compareTo(p2.getName());// 此处只能调用父类的方法,子类的特有方法将报错。        }    }    class Person {        private String name;        Person(String name) {            this.name = name;        }        public String getName() {            return name;        }    }    class Student extends Person {        Student(String name) {            super(name);        }    }    class Worker extends Person {        Worker(String name) {            super(name);        }    }
  • 运行结果:

二,反射

1. 概述

  • 反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。
  • 动态获取类中信息,就是java反射。可以理解为对类的解剖。如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。
  • 所谓的框架就是对外提供一些接口,也就是功能扩展的标准,由实现类按照这个接口标准去实现。框架内部如果需要操纵这些实现类的对象完成某些操作,那么只需要把这些实现类的全名(包名+类名)写在某个配置文件中,框架代码只需要读取这个配置文件,就可以获取这个实现类的字节码文件,然后利用反射技术创建这个实现类的对象并且调用相应的方法完成一些操作。
  • 用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。

2. 基本方法:

  • static Class<?> forName(String className)返回与带有给定字符串名的类或接口相关联的 Class 对象。
  • T newInstance() 创建此 Class 对象所表示的类的一个新实例。

  • Constructor<T> getConstructor(Class<?>... parameterTypes)返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

    Constructor类(位于java.lang.reflect包中)AccessibleObject的子类。

    • T newInstance(Object… initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

  • Field getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。只能获取所有可访问公共字段,private
    获取不到。
  • Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 可以获取到公共字段,也可以获取到私有字段。

    Field类(位于java.lang.reflect包中)AccessibleObject的子类。

    • void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。可对私有字段的访问取消权限检查,暴力访问。
    • void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
    • Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。

  • Method getMethod(String name, Class<?>... parameterTypes)返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
  • Method[] getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
  • Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

    Method类(位于java.lang.reflect包中)AccessibleObject的子类。

    • Object invoke(Object obj, Object… args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。 如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。

3. 获取字节码文件对象(3种)

第一种方式:
  • Object类中的getClass()方法
    • 想要用这种方式,必须要明确具体的类,并创建对象,较麻烦。
  • 示例:Class clazz = new Person().getClass();
第二种方式:
  • 任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
    • 相对简单,但是还是要明确用到类中的静态成员。还是不够扩展。
  • 示例:Class clazz = Person.class;
第三种方式:
  • 通过给定的类的字符串名称就可以获取该类

    • 使用Class类中的forName方法,该方法只要有名称即可,更为方便,扩展性更强。可以把类的字符串名称写到配置文件中,然后读取出来
  • 示例:Class clazz = Class.forName(mypack.Person);

4. 获取Class中的构造函数

获取空参构造函数:
  1. 找寻该文件类文件,调用forName方法并加载进内存,并产生Class对象
  2. 调用newInstance方法,返回一个对象
    示例:
//Person p = new Person();Class clazz = Class.forName(mypack.Person);Object obj = clazz.newInstance();//调用Person的空参构造函数
获取指定构造函数:
  • 既然是通过指定的构造函数进行对象的初始化。所以应该先获取到该构造函数,通过字节码文件对象即可完成。该方法是:getConstructor(parameterTypes);

    Constructor类(位于java.lang.reflect)的T newInstance(Object...initargs)方法使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

  • 示例:

//Person p = new Person("小强",39);Class clazz = Class.forName(mypack.Person);//获取到了指定的构造函数对象Constructor constructor =clazz.getConstructor(int.class,String.class);//通过该构造器对象的newInstance方法进行对象的初始化。Object obj = constructor.newInstance(38,"小明");

5. 获取Class的字段

  • 获取到字节码文件后,调用getField方法获取公共字段和getDeclaredField方法获取所有字段,包括私有。
  • 代码示例:
Class clazz = Class.forName(mypack.Person);//getField只能获取所有可访问公共字段,private获取不到。//Field field = claszz.getField("age");//getDeclaredField可以获取到公共字段,也可以获取到私有字段。Field field = clazz.getDeclaredField("age");//对私有字段的访问取消权限检查,暴力访问。field.setAccessible(true);Object obj = clazz.newInstance();//为对象的属性赋值field.set(obj,89);//获取某对象的某属性值Object o = field.get(obj);

6. 获取Class中的方法

  • 获取到字节码文件后,调用getMethods方法获取公有方法和getDeclaredMethods方法获取所有方法,包括私有。
  • 代码示例:
Class clazz = Class.forName(mypack.Person);//获取有参构造Constructor c =clazz.getConstructor(int.class,String.class);Person p = (Person)c.newInstance(37,"小明");//Method[] methods = clazz.getMethods();//获取的都是公有的方法//methods = clazz.getDeclaredMethods();//只获取本类中所有方法,包括私有。Method m1 = clazz.getMethod("study");m1.invoke(p);Method m2 = clazz.getMethod("show",String.class,int.class);m2.invoke(p,"小强",30);

7. 榨汁机(Juicer)榨汁的案例

package mypack;import java.io.FileInputStream;import java.io.IOException;import java.util.Properties;public class ReflectDemo {    /**     * * 榨汁机(Juicer)榨汁的案例     * 分别有水果(Fruit)苹果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze)     * @throws IOException      */    public static void main(String[] args) throws Exception {         //创建榨汁机        Juicer j = new Juicer();                                        //读取配置文件信息        FileInputStream fis = new FileInputStream("config.properties");        Properties prop = new Properties();        prop.load(fis);        for (int i = 0; i < prop.size(); i++) {            String name = prop.getProperty("Jui"+(i+1));            Class clazz = Class.forName(name);                  //获取该类的字节码文件            Fruit f = (Fruit) clazz.newInstance();              //创建实例对象            j.run(f);        }    }}interface Fruit {    public void squeeze();}class Apple implements Fruit {    public void squeeze() {        System.out.println("榨出一杯苹果汁儿");    }}class Orange implements Fruit {    public void squeeze() {        System.out.println("榨出一杯橘子汁儿");    }}class Banana implements Fruit {    public void squeeze() {        System.out.println("榨出一杯香蕉汁儿");    }}class Juicer {    public void run(Fruit f) {        f.squeeze();    }}
  • 配置文件:

  • 运行结果:

三,正则

1. 概述

  • 正则表达式:符合一定规则的表达式。
  • 作用:用于专门操作字符串。
  • 特点:用于一些特定的符号来表示一些代码操作。这样就简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。
    • 好处:可以简化对字符串的复杂操作。
    • 弊端:符号定义越多,正则越长,阅读性越差。

2. 正则表达式常用构造摘要

字符类
  • [abc] a、b 或 c(简单类)
  • [^abc] 任何字符,除了 a、b 或 c(否定)
  • [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
预定义字符类
  • . 任何字符(与行结束符可能匹配也可能不匹配)
  • \d 数字:[0-9]
  • \D 非数字: [^0-9]
  • \s 空白字符:[ \t\n\x0B\f\r]
  • \S 非空白字符:[^\s]
  • \w 单词字符:[a-zA-Z_0-9]
  • \W 非单词字符:[^\w]
Greedy 数量词
  • X? X,一次或一次也没有
  • X* X,零次或多次
  • X+ X,一次或多次
  • X{n} X,恰好 n 次
  • X{n,} X,至少 n 次
  • X{n,m} X,至少 n 次,但是不超过 m 次
边界匹配器
  • ^ 行的开头
  • $ 行的结尾
  • \b 单词边界
  • \B 非单词边界
  • \A 输入的开头
  • \G 上一个匹配的结尾
  • \Z 输入的结尾,仅用于最后的结束符(如果有的话)
  • \z 输入的结尾

3. 具体操作功能:

匹配:
  • String类中的 boolean matches(String regex);告知此字符串是否匹配给定的正则表达式。 用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false。
  • 示例:
//匹配手机号码是否正确String tel = "15800001111";String regex = "1[358]\\d{9}";boolean b = tel.matches(regex);//true
切割:
  • String;类中的 String[] split(String regex); 方法根据给定正则表达式的匹配拆分此字符串。
    用.进行切割;若不加”\”,代表任何字符,加一个”\”,代表正则表达式中的”.”
  • 示例:
String str = "zhangsanttttxiaoqiangmmmmmzhaoliu";//(.)表示一组,\\1+表示与第1组相同的出现1次以上String[] names = str.split("(.)\\1+");
替换:
  • String类中的String replaceAll(String regex, String replacement);使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 如果regex中有定义组,可以在第二参数中通过$符号获取正则表达式中的已有的组。
  • 示例:
String str = "zhangsanttttxiaoqiangmmmmmzhaoliu";//将重叠的字符替换成单个字母。str = str.replaceAll("(.)\\1+","$1");////采用$1获取组,$表示前一个参数的第一组String str1 = "15800001111";str1 = str1.replaceAll("(\\d{3})(\\d{4})(\\d{4})","$1****$2");//158****0000
获取:
  • 操作步骤:
    1. 将正则表达式封装成对象。

      Pattern类(位于java.util.regex包)正则表达式的编译表示形式。

      • static Pattern compile(String regex) 将给定的正则表达式编译到模式中。
      • Matcher matcher(CharSequence input) 创建匹配给定输入与此模式的匹配器。
    2. 让正则对象和要操作的字符串相关联。获取匹配器对象。
      Matcher类(位于java.util.regex包)通过解释 Pattern 对 character sequence 执行匹配操作的引擎。
      • boolean find() 尝试查找与该模式匹配的输入序列的下一个子序列。
      • String group() 返回由以前匹配操作所匹配的输入子序列。
      • int start() 返回以前匹配的初始索引。
      • int end() 返回最后匹配字符之后的偏移量。
    3. 关联后,获取正则匹配引擎。
    4. 通过引擎对复合规则的子串进行操作,比如取出。
  • 示例:
import java.util.regex.*;class RegexDemo {    public static void main(String[] args) {        getDemo();    }    public static void getDemo() {        String str ="ming tian jiu ke yi xiu xi le ,haha";        System.out.println(str);        String reg = "\\b[a-z]{4}\\b";//\b单词边界        //将规则封装成对象。        Pattern p = Pattern.compile(reg);        //让正则对象和要作用的字符串相关联。获取匹配器对象。        Matcher m = p.matcher(str);        System.out.println(m.matches());//其实String类中的matches方法。用的就是Pattern和Matcher对象来完成的。                                        //只不过被String的方法封装后,用起来较为简单,但是功能单一。        //boolean b = m.find();//将规则作用到字符串上,并进行符合规则的子串查找。        //System.out.println(b);        //System.out.println(m.group());//用于获取匹配后结果。        while (m.find()) {            System.out.println(m.group());            System.out.println(m.start()+"..."+m.end());        }    }}
  • 运行结果:

0 0
原创粉丝点击