反射、正则表达式
一、概述
反射:
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简单一句话:反射就是将java类中的各种成分映射成相应的类
1、反射的基石——Class类
a、所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。
b、Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。
而field中有修饰符、类型、变量名等复杂的描述内容,因此也可以将字段封装称为一个对象。用来获取类中field的内容,这个对象的描述叫Field。
c、Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。每一个字节码就是class的实例对象。如:classcls=Data.class;
同理方法和构造函数也被封装成对象Method、Constructor。要想对一个类进行内容的获取,必须要先获取该字节码文件的对象
P.S.
字节码
当源程序中用到类时,首先要从硬盘把这个类的那些二进制代码,一个类编译成class放在硬盘上以后,就是一些二进制代码,要把这些二进制代码加载到内存中里面来,再用这些字节码去复制出一个一个对象来。
2、Class和class的区别
a、class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。
b、Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。
Person类
public class Person {
public int num;
private String name;
private int age;
static String cou="cn";
public Person(){
super();
System.out.println("null.person run");
}
public Person(String name,int age){
this.name=name;
this.age=age;
System.out.println(name+":"+age+"person run");
}
public void show(){
System.out.println(name + "...show run..." + age);
}
private void privateMethod(){
System.out.println("privatemethod 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...");
}
public int getAge(){
return age;
}
}
反射的基本应用
package cn.swu;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws Exception,NoSuchMethodException{
Person p=new Person("zhangsan",23);
//获取字节码文件方法一:通过对象获取
Class cla0=p.getClass();
//获取字节码文件方法二:通过类来获取
Class cla1=Person.class;
//获取字节码文件方法三:通过给定类的字符串名称(最方便)
Class cla2=Class.forName("cn.swu.Person");
System.out.println(cla0==cla1);
System.out.println(cla1==cla2);
System.out.println("-----------------------------");
//用反射获取空参数的构造方法,并创建对象
Person p0=(Person)cla2.newInstance();
System.out.println("-----------------------------");
//用反射获取带参的构造方法(构造方法必须被public修饰)
Constructor con=cla2.getConstructor(String.class,int.class);
//用反射调用带参的构造方法,创建对象
Object obj=con.newInstance("zhangsan",23);
System.out.println("-----------------------------");
//获取public修饰的字段
System.out.println(cla2.getField("num").toString());
System.out.println("-----------------------------");
//获取所有类型包括私有的字段
Field[] field=cla2.getDeclaredFields();
for (Field f:field){
System.out.println(f.toString());
}
System.out.println("-----------------------------");
//私有的字段是无法赋值的,只有通过以下的暴力访问
field[2].setAccessible(true);
field[2].setInt(p0, 20);
System.out.println(p0.getAge());
System.out.println("-----------------------------");
//获取所有本类的的方法
Method[] methods=cla2.getDeclaredMethods();
for(Method m:methods){
System.out.println(m);
}
System.out.println("-----------------------------");
//调用空参数的一般函数
Method method0=cla2.getMethod("show",null);
method0.invoke(obj,null);
System.out.println("-----------------------------");
//调用带参数的一般函数
Method method1=cla2.getMethod("paramMethod", String.class,int.class);//获取函数
System.out.println(method1);
method1.invoke(obj, "wangwu",30);//调用函数
}
}
运行结果:
二、反射的应用
一个已经可以使用的应用程序,因为程序已经做好可以运行使用,不能再进行代码的加入了。而当后期我们新的功能加入程序时,该怎么做呢?就如我们的电脑一样,后期我们可能会鼠标、键盘等,所以电脑给我们预留了usb接口,只要符合这个接口规则的设备,电脑就可以通过加载驱动等操作来使用。
那这个程序能用了,如何使用后期出现的功能类呢?
常用的作法,会提供一个配置文件,来供以后实现此程序的类来扩展功能。对外提供配置文件,让后期出现的子类直接将类名字配置到配置文件中即可。该应用程序直接读取配置文件中的内容。并查找和给定名称相同的类文件。进行如下操作:
1)加载这个类。
2)创建该类的对象。
3)调用该类中的内容。
应用程序使用的类不确定时,可以通过提供配置文件,让使用者将具体的子类存储到配置文件中。然后该程序通过反射技术,对指定的类进行内容的获取。
好处:反射技术大大提高了程序的扩展性。
USB.java
package cn.swu;
//定义接口规则
public interface USB {
public void run();
public void stop();
}
Mouse.java
package cn.swu;
//鼠标类,实现规则
public class Mouse implements USB {
public void run(){
System.out.println("mouse run...");
}
public void stop(){
System.out.println("mouse stop......");
}
}
Keyboard.java
package cn.swu;
//键盘类,实现USB规则
public class Keyboard implements USB {
public void run(){
System.out.println("Keyboard run...");
}
public void stop(){
System.out.println("Keyboard stop......");
}
}
Mypad.java
package cn.swu;
//Mypad类,用来加载USB
public class Mypad {
public void run(){
System.out.println("Mypad is RUNNING.....");
}
public static void useUSB(USB u)throws Exception{
u.run();
Thread.currentThread().sleep(2000);
u.stop();
}
}
RefleciTest.java
package cn.swu;
//从配置文件中读取后期扩展的USB,并加载运行
import java.io.*;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args)throws Exception {
Mypad pad=new Mypad();
pad.run();
//配置文件关联输入流
FileInputStream fi=new FileInputStream("USB.properties");
Properties pop=new Properties();
//从输入流中读取属性列表
pop.load(fi);
//Class cla0=Class.forName("cn.Mouse");
for(int i=0;i<pop.size();i++){
//获取配置文件中对应的类名
String USBname=pop.getProperty("usb"+i);
//获取该类的字节码文件对象
Class cla=Class.forName(USBname);
//利用反射,创建对象
USB usb=(USB)cla.newInstance();
//加载USB
pad.useUSB(usb);
}
fi.close();
}
}
配置文件
USB.properties
usb0=cn.swu.Mouse
usb1=cn.swu.Keyboard
运行结果:
正则表达式
正则表达式:符合一定规则的表达式,专用于操作字符串
特点:用一些特定的符号来表示一些代码操作,简化书写
1、匹配:String类中的matches(String regex),判断是否和指定的正则表达式匹配
package cn.swu1;
//用正则表达判断qq号是否合法,第一位不能为0,共5-15位
public class Test2 {
public static void main(String[] args) {
String QQ="02345565";
String reg="[19][0-9]{1,14}";//[第一位1-9][0-9]出现{1-14次}
System.out .println(QQ.matches(reg));
}
}
2、切割:String 类中的
split(String regex, int limit)
,根据匹配给定的正则表达式来拆分此字符串package cn.swu1;
//用正则表达判断切割字符串
public class Test2 {
public static void main(String[] args) {
splitDemo("c:\\a\\b.txt","\\\\");
//按照叠词切,需要重用规则的结果
//将规则封装成一个组用()完成。且每个组都有一个编号,从1开始
//想要使用已有的组可以通过\n(n为组编号)的形式来获取
splitDemo("assgoegggsdfqqq","(.)\\1\\1");//需要转义\所以要两个\
}
public static void splitDemo(String str,String reg){
String[] str1=str.split(reg);
for(String s:str1){
System.out.println(s);
}
}
}
运行结果:
3、替换:String 类中的
replaceAll(String regex,String replacement)
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。package cn.swu1;
//用正则表达判断替换字符串
public class Test2 {
public static void main(String[] args) {
//将叠词用一个词替代
//$编号:使用前一规则中对应编号的组
replaceDemo("assgoegggsdfqqq","(.)\\1+","$1");//$1就分别代表s、g、q
}
public static void replaceDemo(String str,String reg,String newStr){
String s=str.replaceAll(reg, newStr);
System.out.println(s);
}
}
操作步骤:
1、将正则表达式封装成对象
2、让正则表达式和要操作的字符串相关联
3、关联后,获取正则匹配引擎
4、通过引擎对符合规则的字符串取出
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 用正则表达式判断将符合规则的子串取出
1、将正则表达式封装成对象
2、让正则表达式和要操作的字符串相关联
3、关联后,获取正则匹配引擎
4、通过引擎对符合规则的字符串取出*/
public class Test2 {
public static void main(String[] args) {
//将单词长度为3的单词找出
findDemo("some one like you 666 !","\\b(.){3}\\b");//"\b"表示单边界的意思
}
public static void findDemo(String str,String reg){
//Tep1:将指定的正则表达式封装成对象
Pattern p=Pattern.compile(reg);
//Tep2:让正则表达式对象和字符串相关联,并获取匹配引擎
Matcher m=p.matcher(str);
//Tep3:通过匹配引擎找到匹配的字符串
while(m.find()){
System.out.println(m.group());
System.out.println(m.start()+"..."+m.end());//m.start和m.end为对应的角标
}
}
}
运行结果:
/*
* 将字符串"我我我...我..我要要要...要..学学学...学..学编.......编..编编...编.编...程...程....程程程"
* 更正为我要学编程输出
*/
public class Test2 {
public static void main(String[] args) {
String str="我我我...我..我要要要...要..学学学...学..学编.......编..编编...编.编...程...程....程程程";
//将点去掉="."替换为""
str=str.replaceAll("[.+]", "");
//将叠字去掉
str=str.replaceAll("(.)\\1+", "$1");
System.out.println(str);
}
}
import java.io.*;
import java.util.TreeSet;
/*
需求: 把以下IP存入一个txt文件,编写程序把这些IP按数值大小,从小到达排序并打印出来。
134.54.231.245
61.54.231.9
192.54.231.246
61.54.231.48
45.53.231.249
思路:
1、为了让ip可以按照字符串顺序比较,只要让ip的每一段的位数相同,将不足位补零
*/
public class Test2 {
public static void main(String[] args)throws Exception {
BufferedReader br=new BufferedReader(new FileReader("IP.txt"));
TreeSet ts=new TreeSet();
String line=null;
while((line=br.readLine())!=null){
line=line.replaceAll("\\b(\\d)", "00$1");//每一段中都补两个零
line=line.replaceAll("0*(\\d{3})", "$1");//每一段都只保留后三位
ts.add(line);//将字符串存入treeSet集合,使用TreeSet集合自然排序的功能
}
for(Object o:ts){
String str=o.toString().replaceAll("[0]+(\\d)", "$1");//将排好序的字符串多余的零去掉
System.out.println(str);
}
}
}
运行结果:【邮箱爬虫】
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
需求: 将一个文本中的邮箱全部打印到控制台上。
思路:
1、读取文本文件,用正则表达式判断是否为文件格式
*/
public class Test2 {
public static void main(String[] args)throws Exception {
BufferedReader br=new BufferedReader(new FileReader("mail.txt"));//读取存有邮箱的文本文件
String line=null;
String reg="\\w+@\\w+(\\.\\w+)+";//定义邮箱格式的正则表达式
Pattern p=Pattern.compile(reg);//将正则表达式封装成对象
while((line=br.readLine())!=null){
Matcher m=p.matcher(line);//获取正则表达式和字符串相关联的引擎
while(m.find()){
System.out.println(m.group());
}
}
}
}
运行结果