反射机制:
java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用他的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
说简单点:动态获取类中的信息,就是java的反射。可以理解为对类的剖析。
反射技术提高了应用的扩展性,而且应用简单,所以很常用。
想要对一个类文件进行解剖,只要获取该类的字节码文件对象即可。怎么获取呢?
获取字节码对象的方式:
package day28;
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 day28;
public class ReflectDemo1 {
/**
* 获取字节码对象的方式:
* 1.Object类中的getClass()方法。
* 想要用这种方式,必须要明确具体的类,并创建对象。
* 2.任何数据类型都具备一个静态的属性.class来获取其对应的class对象。
* 这种方法相对简单,但还是需要明确用到类中的静态成员,还是不够扩展。
* 3.可以用Class类中forName方法完成。
* 只要通过给定的类的字符串名称就可以获取该类,更为方便,扩展性更强。
* @param args
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException {
getClassObject_3();
}
public static void getClassObject_3() throws ClassNotFoundException {
String className="day28.Person"; //注意这里得带着包名,否则就异常了。
Class clazz=Class.forName(className);
System.out.println(clazz);
}
public static void getClassObject_2() {
Class clazz=Person.class;
Class clazz1=Person.class;
System.out.println(clazz==clazz1);
}
public static void getClassObject_1() {
Person p=new Person();
Class clazz=p.getClass();
Person p1=new Person();
Class clazz1=p.getClass();
System.out.println(clazz==clazz1);
}
}
获取字节码文件的构造函数,并创建对象。
package day28;
import java.lang.reflect.Constructor;
public class ReflectDemo2 {
/**
* 获取字节码文件的构造函数,并创建对象。
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
createNewObject_2();
}
public static void createNewObject_2() throws Exception {
//以前这样就能创建带参的对象。
Person p=new Person("小强",39);
/* 现在:
* 当获取指定名称对应类中的所体现的对象时,而该类对象初始化不使用空参数构造该怎么办呢?
* 既然是通过指定的构造函数进行对象的初始化,就要先获取到该构造函数。如何获取?
* 通过字节码文件对象的方法即可完成。该方法是:getConstructor(paramterTypes);
*/
String name="day28.Person";
Class clazz=Class.forName(name);
Constructor constructor=clazz.getConstructor(String.class,int.class);
Object obj=constructor.newInstance("小明",38);
}
public static void createNewObject_1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//以前这么写,new的时候,先根据被new的类的名称找寻改类的字节码文件,
//然后加载进内存,并创建该字节码文件对象,接着创建该字节码文件对应的person对象。
// Person p=new Person();
//现在
String name="day28.Person";
//找寻该名称的类文件,并加载进内存,并产生class对象。
Class clazz=Class.forName(name);
//如何产生该类的对象呢?newInstance方法可以产生空参的对象,但只能是空参。
Object obj=clazz.newInstance();
}
}
获取字节码文件中的字段。
package day28;
import java.lang.reflect.Field;
public class ReflectDemo3 {
/**
* 获取字节码文件中的字段。
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
getFiledDemo();
}
public static void getFiledDemo() throws Exception {
Class clazz=Class.forName("day28.Person");
//Field field=clazz.getField("age"); //只能获取共有的,private修饰的拿不到。
Field field=clazz.getDeclaredField("age"); //只获取本类,但可以获取到私有的内容。
field.setAccessible(true); //对私有字段的访问要取消权限检查。暴力访问。
Object obj=clazz.newInstance();
field.set(obj,89);
Object o=field.get(obj);
System.out.println(o);
}
}
获取字节码文件中的函数。
package day28;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo4 {
/**
* 获取字节码文件中的函数。
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
getMethodDemo_3();
}
public static void getMethodDemo_3() throws Exception, Exception {
Class clazz=Class.forName("day28.Person");
Method method=clazz.getMethod("paramMethod",String.class,int.class);
Object obj=clazz.newInstance();
method.invoke(obj, "小强",89);
}
public static void getMethodDemo_2() throws Exception {
Class clazz=Class.forName("day28.Person");
Method method=clazz.getMethod("show",null); //获取空参数一般方法。
Constructor constructor=clazz.getConstructor(String.class,int.class);
Object obj=constructor.newInstance("小明",37);
method.invoke(obj, null);
}
public static void getMethodDemo() throws Exception {
Class clazz=Class.forName("day28.Person");
Method[] methods=clazz.getMethods(); //获取的都是共有的方法
Method[] methods1=clazz.getDeclaredMethods(); //只获取本类中的所有方法,包含私有
for(Method method:methods1){
System.out.println(method);
}
}
}
反射的实际应用:
package day28;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
public class ReflectTest {
/**
* 电脑运行事例。
* 涉及到的类:MainBoard SoundCard PCI
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
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 String PCIName=prop.getProperty("PCI"+(x+1)); Class clazz=Class.forName(PCIName); //用class去加载PCI的子类 Object obj=clazz.newInstance(); PCI p=(PCI)clazz.newInstance(); mb.usePCI(p); } fis.close(); } } package day28; public class MainBoard { public void run(){ System.out.println("main board run..."); } public void usePCI(PCI p){ //PCI p=new SoundCard() if(p!=null){ p.open(); p.close(); } } } package day28; public interface PCI { public void open(); public void close(); } package day28; import day28.PCI; public class SoundCard implements PCI { public void open() { System.out.println("Sound Open"); } public void close() { System.out.println("Sound Close"); } } 配置文件:PCI.properties PCI1=day28.SoundCard //这下电脑要填网卡啊啥的,直接在配置文件一写就行了,不用改源代码了。 正则表达式:正确规则的表达式 正则表达式主要用于操作字符串数据。它是通过一些特定的符号来体现的。 所以为了掌握正则表达式,必须要学习并掌握这些符号。 正则表达式虽然将代码简化了,但是阅读性变差了。 package day28; public class RegexDemo { /** * 正则表达式:正确规则表达式 * 正则表达式主要用于操作字符串数据。 */ public static void main(String[] args) { /* * 需求:定义一个功能对qq号进行校验。 * qq号的要求:长度5-15 , 只能是数字 ,0不能开头 */ String qq="1234556jkl369"; //checkQQ(qq); String regex="[1-9][0-9]{4,14}"; //正则表达式 boolean b=qq.matches(regex); System.out.println(qq+":"+b); } public static void checkQQ(String qq){ int len=qq.length(); if(len>=5&&len<=15){ if(!qq.startsWith("0")){ try{ long l=Long.parseLong(qq); System.out.println(l+"正确!"); }catch(NumberFormatException e){ System.out.println(qq+":含有非法字符"); } }else{ System.out.println(qq+":不能以0开头"); } }else{ System.out.println(qq+":长度错误"); } } } package day28; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexDemo2 { public static void main(String[] args) { /* * 正则表达式对字符串的常见操作: * 1.匹配 * 其实使用的就是String类中的matches方法。 * 2.切割 * 其实使用的就是String类中的split方法。 * 组:(A(B(C))) 从左括号数,依次为第一组,第二组.... * 3.替换 * 其实使用的就是String类中的replaceAll方法 * 4.获取 * 将正则规则进行对象的封装。 * Pattern p = Pattern.compile("a*b"); * 通过正则对象的matcher方法和字符串关联,获取要对字符串操作的匹配器对象Matcher。 * Matcher m = p.matcher("aaaaab"); * 通过Matcher匹配器对象的方法对字符串进行操作。 * boolean b = m.matches(); * */ functionDemo_4(); } public static void functionDemo_1(){ //匹配手机号码是否正确 String tel="15800001111"; String regex="1[358]\\d{9}"; //这里要两个反斜线+d,第一个是把第二个转义,让它变成反斜线的意思 boolean b=tel.matches(regex); System.out.println(tel+":"+b); } public static void functionDemo_2(){ String str="zhangsan****xiaoqiang########zhaoliu"; String regex="(.)\\1+"; //括号(.)就是把.封装成组,这里默认就是第一组,然后引用第一组。 String[] names=str.split(regex); for(String name:names){ System.out.println(name); } } public static void functionDemo_3(){ String str="zhangsan****xiaoqiang########zhaoliu"; str=str.replaceAll("(.)\\1+", "$1"); //$1表示获取前面参数的第一组 System.out.println(str); String tel="15800001111"; tel=tel.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2"); //这就等于把第一组和第二组留下,中间换成* System.out.println(tel); } public static void functionDemo_4(){ //获取长度为三个字母的词 String str="da jia hao,ming tian bu fang jia"; String regex="\\b[a-z]{3}\\b"; //两边得有单词边界,否则就把四个五个字母的词也获取到了,只不过获取的是前三个字母,但这并不是我们想要的 System.out.println(str); //1.将正则封装成对象 Pattern p=Pattern.compile(regex); //2.通过正则对象获取匹配器对象 Matcher m=p.matcher(str); //3.使用Matcher对象的方法对字符串进行操作。 //既然要获取三个字母组成的单词,用查找方法find() while(m.find()){ System.out.println(m.group()); //获取匹配的子序列 System.out.println(m.start()+":"+m.end()); //获取位置 } } } 正则表达式的几个练习: package day28; import java.util.TreeSet; public class RegexTest { /** * 练习: * 1.治疗口吃:我我...我..我...我我要..要...要要..要要学.学学...学学.学编...编编...编编编..编程.程.程.程 * 2.对IP地址排序。 * 3.对邮件地址校验 * @param args */ public static void main(String[] args) { test_1(); test_2(); test_3(); } public static void test_3() { String mail="[email protected]"; String regex="[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3}){1,3}"; boolean b=mail.matches(regex); System.out.println(mail+":"+b); } public static void test_2() { String ip_str="192.168.10.34 127.0.0.1 3.3.3.3 105.70.11.55"; //为了让IP可以按照字符串顺序比较,只要每段位数相同即可。 //所以就要补0。最多补两个0,所以每段前面都加两个0。然后每段保留后3位即可。 ip_str=ip_str.replaceAll("(\\d+)", "00$1"); ip_str=ip_str.replaceAll("0*(\\d{3})", "$1"); String[] ips=ip_str.split(" +"); TreeSet for(String ip:ips){ ts.add(ip); } for(String ip:ts){ System.out.println(ip.replaceAll("0*(\\d+)","$1" )); } /* 这么做可以排序,但结果不对3.3.3.3应该是最小,排在最前面。 * 但是却排在了最后,但只要变成003.003.003.003就可以完成了,所以就要补0. String[] ips=ip_str.split(" +"); TreeSet for(String ip:ips){ ts.add(ip); } for(String ip:ts){ System.out.println(ip); } * */ } public static void test_1(){ String str="我我...我..我...我我要..要...要要..要要学.学学...学学.学编...编编...编编编..编程.程.程.程"; //1.将字符串中的.去掉。用替换 str=str.replaceAll("\\.", ""); //2.替换叠词 str=str.replaceAll("(.)\\1+", "$1"); System.out.println(str); } } //实现 网页爬虫功能。 package day28; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest2 { /** * 网页爬虫。 * 就是一个程序,用于在互联网中获取符合指定规则的数据。 * * 爬取邮箱地址。 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { List for(String mail:list){ System.out.println(mail); } } public static List //1.读取源文件 BufferedReader bufr=new BufferedReader(new FileReader("mail.html")); /* * 如果要源文件是网络上的一个网页,稍微修改就行。 * URL url=new URL("网址"); * BufferedReader bufIn=new BufferedReader(new InputStreamReader(url.openStream())); * 再把下面的bufr换成bufIn就行。 */ //2.对读取的数据进行规则的匹配,从中获取符合规则的数据。 String mail_regex="\\w+@\\w+(\\.\\w+)+"; List Pattern p=Pattern.compile(mail_regex); String line=null; while((line=bufr.readLine())!=null){ Matcher m=p.matcher(line); //3.将符合规则的数据存储到集合中。 while(m.find()){ list.add(m.group()); } } return list; } }