10、黑马程序员——反射+正则

来源:互联网 发布:淘宝店铺发布宝贝品牌 编辑:程序博客网 时间:2024/05/29 07:24

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------



10.1反射


JAVA反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象 方法的功能称为java语言的反射机制。
动态获取类中信息,就是java反射。可以理解为对类的解剖。
如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。

注:
所谓的框架就是对外提供一些接口,也就是功能扩展的标准,由实现类按照这个接口标准去实现。框 架内部如果需要操纵这些实现类的对象完成某些操作,那么只需要把这些实现类的全名(包名+类名)写在某个配置文件中,框架代码只需要读取这个配置文件,就可以获取这个实现类的字节码文件,然后利 用反射技术创建这个实现类的对象并且调用相应的方法完成一些操作。


用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一 般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件 进行解剖,只要获取到该类的字节码文件对象即可。
获取Class对象的三种方式
加载XX.class文件进内存时就被封装成了对象,该对象就是字节码文件对象。如何获取Class对象呢?
方式一:
通过对象的getClass方法进行获取。
如:Class clazz=new Person().getClass();//Person是一个类名
麻烦之处:每次都需要具体的类和该类的对象,以及调用getClass方法。
方式二:
任何数据类型都具备着一个静态的属性class,这个属性直接获取到该类型的对应Class对象。
如:Class clazz=Person.class;//Person是一个类名
比第一种较为简单,不用创建对象,不用调用getClass方法,但是还是要使用具体的类,和该类中的一个静态属性class完成。
方式三:
这种方式较为简单,只要知道类的名称即可。不需要使用该类,也不需要去调用具体的属性和行为。就可以获取到Class对象了。
如:Class clazz=Class.forName("包名.Person");//Person是一个类名
这种方式仅知道类名就可以获取到该类字节码对象的方式,更有利于扩展

package day10;public class Person {private int age;private String name;public Person(String name,int age){super();this.age = age;this.name = name;System.out.println("Person param run..." + this.name + ":" + this.age);}public Person(){super();System.out.println("person run");}public void show(){System.out.println(name + "...show run ..." + age);}private void privateMethod(){System.out.println("method run");}public void paramMethod(String str, int num){System.out.println("paramMethod run..." + str + ":" + num);}public static void staticMethod(){System.out.println("static method run...");}}
package day10;//要想要对字节码文件进行解剖,必须要有字节码文件对象public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {getClassObject_1();System.out.println("------");getClassObject_2();System.out.println("-------");getClassObject_3();}/* * 获取字节码对象的方式:     * 方式一:Object类中的getClass()方法的。 * 想要用这种方式,必须要明确具体的类,并创建对象。     * 麻烦。 */public static void getClassObject_1(){Person p = new Person();Class clazz = p.getClass();Person p1 = new Person();Class clazz1 = p1.getClass();System.out.println(clazz == clazz1);}/* * 方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。 * 相对简单,但是还是要明确用到类中的静态成员。 * 还是不够扩展。 */public static void getClassObject_2(){Class clazz = Person.class;Class clazz1 = Person.class;System.out.println(clazz == clazz1);}/* * 方式三:只要通过给定的类的字符串名称就可以获取该类,更为扩展。 * 可以用Class类中的方法完成。 * 该方法就是forName。 * 这种方法只要有名称即可,更为方便,扩展性更强。 */ public static void getClassObject_3() throws ClassNotFoundException{ //可以把类的字符串名称写到配置文件中,然后读取出来。 String className = "day10.Person"; Class clazz = Class.forName(className); System.out.println(clazz); }}


Constructor类

1、概述
如果指定的类中没有空参数的构造函数,或者要创建的类对象需要通过指定的构造函数进行初始化。这时怎么办呢?这时就不能使用Class类中的newInstance方法了。既然要通过指定的构造函数进行对象的初始化。就必须先获取这个构造函数——Constructor。Constructor代表某个类的构造方法。
2、获取构造方法:
1)得到这个类的所有构造方法:如得到上面示例中Person类的所有构造方法
Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();
2)获取某一个构造方法:
Constructor con=Person.class.getConstructor(String.class,int.class);
3、创建实例对象:
1)通常方式:Person p = new Person(“lisi”,30);
2)反射方式:Person p= (Person)con.newInstance(“lisi”,30);

package day10;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;//获取Class中的构造函数public class ReflectDemo1 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {createNewObject_1();System.out.println("-----------");createNewObject_2();}public static void createNewObject_1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{/*之前,new的时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存, * 并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象 * Person p=new Person(); *  * 现在,用Class对象来获取类实例对象的做法 * 先查找并加载指定名字的字节码文件进内存,并被封装成Class对象。 * 通过Class对象的newInstance方法创建该Class对应的类实例。 * 调用newInstance()方法会去使用该类的空参数构造函数进行初始化。 */        String name = "day10.Person";        Class clazz = Class.forName(name);        Object obj = clazz.newInstance();//调用Person的空参构造函数}public static void createNewObject_2() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//Person p = new Person("小强",25);/* * 当获取指定名称对应类中的所体现的对象时。 * 而该对象初始化不使用空参数构造函数该怎么办呢? * 既然是通过指定的构造函数进行对象的初始化。 * 所以应该先获取到该构造函数,通过字节码文件对象即可完成。 * 该方法是:getConstructor(parameterTypes); */String name = "day10.Person";//找寻该名称类文件,并加载进内存,并产生Class对象。Class clazz = Class.forName(name);//获取到了指定的构造函数对象。Constructor constructor = clazz.getConstructor(String.class, int.class);//通过该构造函器对象的newInstance方法进行对象的初始化Object obj = constructor.newInstance("小强",25);}}
注:
1、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。
2、newInstance():构造出一个实例对象,每调用一次就构造一个对象。
3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象


Field类

1、Field类代表某个类中一个成员变量
2、方法
Field getField(String s);//只能获取公有和父类中公有
Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有
setAccessible(ture);
//如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。
set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。
Object get(Object obj);//返回指定对象上Field表示的字段的值。

package day10;import java.lang.reflect.Field;//获取Class中的字段public class ReflectDemo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {getFieldDemo();}//获取字节码文件中的字段。public static void getFieldDemo() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{Class clazz = Class.forName("day10.Person");//getField只能获取所有可访问公共字段,private获取不到。//Field field = clazz.getField("age");//getDeclaredField可以获取到公共字段,也可以获取到私有字段。Field field = clazz.getDeclaredField("age");//对私有字段的访问取消权限检查,暴力访问。field.setAccessible(true);Object obj = clazz.newInstance();//为对象的属性赋值field.set(obj, 88);//获取某对象的某属性值Object o = field.get(obj);System.out.println(field);}}


Method类

1、概述:Method类代表某个类中的一个成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。
2、专家模式:谁调用这个数据,就是谁在调用它的专家。
如人关门:
调用者:是门调用关的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。
指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。
总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。
3、方法
Method[] getMethods();//只获取公共和父类中的方法。
Method[] getDeclaredMethods();//获取本类中包含私有。
Method getMethod("方法名",参数.class(如果是空参可以写null));
Object invoke(Object obj ,参数);//调用方法
如果方法是静态,invoke方法中的对象参数可以为null。
package day10;import java.lang.reflect.Constructor;import java.lang.reflect.Method;//获取Class中的方法public class ReflectDemo3 {public static void main(String[] args) throws Exception {getMethodDemo_1();System.out.println("----------");getMethodDemo_2();System.out.println("-----------");getMethodDemo_3();}/* * 获取指定Class中的公共函数。 */public static void getMethodDemo_1() throws Exception{Class clazz = Class.forName("day10.Person");Method[] methods = clazz.getMethods();//获取的都是公有的方法methods = clazz.getDeclaredMethods();//只获取本类中的所有方法,包括私有。for(Method method : methods){System.out.println(method);}}public static void getMethodDemo_2() throws Exception{Class clazz = Class.forName("day10.Person");Method method = clazz.getMethod("show", null);//获取空参数一般方法。Object obj = clazz.newInstance();Constructor constructor = clazz.getConstructor(String.class,int.class);obj = constructor.newInstance("小明",10);method.invoke(obj, null);}public static void getMethodDemo_3() throws Exception{Class clazz = Class.forName("day10.Person");Method method = clazz.getMethod("paramMethod", String.class,int.class);//获取空参数一般方法Object obj = clazz.newInstance();Constructor constructor = clazz.getConstructor();obj = constructor.newInstance();method.invoke(obj, "小强",77);}}

反射练习:

package day10;public interface PCI {public void open();public void close(); }
<pre name="code" class="java">package day10;public class SoundCard implements PCI{public void open(){System.out.println("sound open");}public void close(){System.out.println("sound close");}}


package day10;public class NetCard implements PCI {public void open(){System.out.println("net open");}public void close(){System.out.println("net close");}}
package day10;public class Mainboard {public void run(){System.out.println("main board run ...");}public void usePCI(PCI p){if(p != null){p.open();p.close();}}}
pci1=day10.SoundCard
pci2=day10.NetCard
package day10;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.util.Properties;public class ReflectTest {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {Mainboard mb = new Mainboard();mb.run();//每次添加一个设备都需要修改代码传递一个新创建的对象//mb.usePCI(new SoundCard());//能不能不修改代码就可以完成这个动作//不用new完成,而是只获取其class文件,在内部实现创建对象的动作。File configFile = new File("pci.properties");Properties prop = new Properties();FileInputStream fis = new FileInputStream(configFile);prop.load(fis);for(int x = 0; x < prop.size(); x++){String pciName = prop.getProperty("pci" + (x+1));Class clazz = Class.forName(pciName);//用 Class去加载这个子类PCI p = (PCI)clazz.newInstance();mb.usePCI(p);}fis.close();}}





10.2正则


概述

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

常见符号

说明:X表示字符X或者匹配的规则。
1、字符
x 字符 x
\\ 反斜线字符
\t 制表符 ('\u0009')
\n 新行(换行)符 ('\u000A')
\r 回车符 ('\u000D')
\f 换页符 ('\u000C')
\a 报警 (bell) 符 ('\u0007')
2、字符类
[abc] a、b或 c(简单类)
[^abc] 任何字符,除了 a、b或 c(否定)
[a-zA-Z] a到 z或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a到 d或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e或 f(交集)
[a-z&&[^bc]] a到 z,除了 b和 c:[ad-z](减去)
[a-z&&[^m-p]] a到 z,而非 m到 p:[a-lq-z](减去)
3、预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
4、边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
5、Greedy数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n次
X{n,} X,至少 n次
X{n,m} X,至少 n次,但是不超过 m 次
6、组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:
1 ((A)(B(C)))
2 \A
3 (B(C))
4 (C)
组零始终代表整个表达式。在替换中常用$匹配组的内容。



正则表达式常见功能:匹配、切割、替换、获取

四种功能的选择(思路方式):
1)如果只想知道该字符是否对是错,使用匹配。
2)想要将已有的字符串变成另一个字符串,替换。
3)想要按照自定的方式将字符串变成多个字符串。切割。获取规则以外的子串。
4)想要拿到符合需求的字符串子串,获取。获取符合规则的子串。

package day10;import java.util.regex.Matcher;import java.util.regex.Pattern;/** 正则表达式对字符串的常见操作* 1. 匹配*其实使用的就是String类中的matches方法。** 2. 切割*其实使用的就是String类中的split方法。** 3. 替换** 4. 获取*/public class RegexDemo {public static void main(String[] args) {functionDemo_1();System.out.println("-----------");functionDemo_2();System.out.println("-----------");functionDemo_3();System.out.println("------------");functionDemo_4();System.out.println("------------");functionDemo_5();System.out.println("------------");functionDemo_6();System.out.println("------------");functionDemo_7();}//演示匹配public static void functionDemo_1(){//匹配手机号码是否正确String tel = "15800001111";String regex = "1[358]\\d{9}";boolean b = tel.matches(regex);System.out.println(tel + ":" + b);}//演示切割public static void functionDemo_2(){String str = "zhangsan xiaoqiang   zhaoliu";String[] names = str.split(" +");for(String name : names){System.out.println(name);}}public static void functionDemo_3(){String str = "zhangsan.xiaoqiang.zhaoliu";String[] names = str.split("\\.");for(String name : names){System.out.println(name);}}/** 在表达式 ((A)(B(C))) 中,存在四个这样的组:* 1 ((A)(B(C)))* 2 \A* 3 (B(C))* 4 (C)* 组零始终代表整个表达式。*/public static void functionDemo_4(){String str = "zhangsanttttxiaoqiangmmmmmzhaoliu";//(.)表示一组,\\1+表示与第一组相同的出现1次以上String[] names = str.split("(.)\\1+");for(String name : names){System.out.println(name);}}//演示替换public static void functionDemo_5(){String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";//将叠词替换为一个,其中$1表示符合组中一个字符 str = str.replaceAll("(.)\\1+","$1");System.out.println(str);}public static void functionDemo_6(){String str = "15800001111";str = str.replaceAll("(\\d{3})(\\d{4})(\\d{4})", "$1****$2");System.out.println(str);}/** 演示获取* 将正则规则进行对象的封装。* Pattern p = Pattern.compile("a*b");* 通过正则对象的matcher方法字符串相关联。获取要对字符串操作的匹配器对象Matcher。* boolean b = m.matches();*/public static void functionDemo_7(){String str = "da jia hao,ming tian bu fang jia";/*Pattern类为正则表达式的编译表示形式。指定为字符串的正则表达式必须首先被编译为此类的实*例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹*配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。*/// \\b表示单词边界String regex = "\\b[a-z]{3}\\b";//1.将正则封装成对象Pattern p = Pattern.compile(regex);//2.通过正则对象获取匹配器对象Matcher m = p.matcher(str);//使用Matcher对象的方法对字符串进行操作。//既然要获取三个字母组成的单词。//查找:find();while(m.find()){System.out.println(m.group());//获取匹配的子序列System.out.println(m.start() + ":" + m.end());}}}
注:
Pattern类为正则表达式的编译表示形式。指定为字符串的正则表达式必须首先被编译为此类的实
例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹
配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。



练习1:
package day10;import java.util.TreeSet;/** 1. 治疗口吃:我我...我我...我我我我...要要要要...要要要要...学学学学学...            学学编编...编编编编..编..编...程程...程程程* 2. 对ip地址排序* 3. 对邮件地址校验*/public class RegexTest1 {public static void main(String[] args) {test1();test2();test3();}//1.治口吃public static void test1(){String str = "我我...我我...我我我我...要要要要...要要要要...学学学学学...学学编编...编编编编..编..编...程程...程程程";          //将字符串中.去掉,用替换。str = str.replaceAll("\\.+","");//替换叠词    str = str.replaceAll("(.)\\1+","$1");System.out.println(str);}/** 2.对ip地址排序。* 192.168.10.34 127.0.0.1 3.3.3.3 105.70.11.55*/public static void test2(){String ip_str = "192.168.10.34 127.0.0.1 3.3.3.3 105.70.11.55";// 为了让ip可以按照字符串顺序比较,只要让ip的每一段的位数相同。//所以,补零,按照每一位所需最多0进行补充,每一段都加两个0。ip_str = ip_str.replaceAll("(\\d+)", "00$1");System.out.println(ip_str);//然后每一段保留数字3位ip_str = ip_str.replaceAll("0*(\\d(3))", "$1");System.out.println(ip_str);//将ip地址切出。String[] ips = ip_str.split(" +");TreeSet<String> ts = new TreeSet<String>();for(String ip : ips){ts.add(ip);}for(String ip : ts){System.out.println(ip.replaceAll("0*(\\d+)", "$1"));}}//3.对邮件地址校验。public static void test3(){String mail = "abc1@sina.com";String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3})+";boolean b = mail.matches(regex);System.out.println(mail + ":" + b);}}

练习2:
package day10;import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;/** 网页爬虫:其实就是一个程序用于在互联网中获取符合指定规则的数据。* 爬取邮箱地址。*/public class RegexTest2 {public static void main(String[] args) throws IOException {List<String> list = getMails();for(String main : list){System.out.println(main);}}public static List<String> getMails() throws IOException{//1.读取源文件。BufferedReader bufr = new BufferedReader(new FileReader("mail.html"));//2.对读取的数据进行规则的匹配,从中获取符合规则的数据。String mail_regex = "\\w+@\\w+(\\.\\w+)+";List<String> list = new ArrayList<String>();Pattern p = Pattern.compile(mail_regex);String line = null;while((line = bufr.readLine()) != null){Matcher m = p.matcher(line);while(m.find()){list.add(m.group());}}//3.将符合规则的数据存储到集合中。return list;}}


练习3:
package day10;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;//网络爬虫(蜘蛛) //实际上是一个功能,用于搜集网络上的指定信息public class RegexTest3 {public static void main(String[] args) throws IOException {List<String> list = getMailsByWeb();for(String mail : list){System.out.println(mail);}}public static List<String> getMailsByWeb() throws IOException{URL url = new URL("http://www.itheima.com/about/1376.html");BufferedReader bufr = new BufferedReader(new InputStreamReader(url.openStream()));String mail_regex = "\\w+@\\w+(\\.\\w+)+";List<String> list = new ArrayList<String>();Pattern p = Pattern.compile(mail_regex);String line = null;while((line = bufr.readLine()) != null){Matcher m = p.matcher(line);while(m.find()){list.add(m.group());}}return list;}}



------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

0 0
原创粉丝点击