反射机制:


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 ts=new 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 ts=new 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 list=getMails();

for(String mail:list){

System.out.println(mail);

}

}

public static List getMails() throws Exception{

//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 list=new ArrayList();

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;

}

}