java学习笔记28

来源:互联网 发布:facebook关闭人工智能 编辑:程序博客网 时间:2024/05/17 22:59

1、反射机制

想扩展功能就必须向外提供接口。


服务器处理用户的请求,执行不同的功能(相当于扩展了功能,因为不同用户的需求,需要对应不同的功能)。本来给Tomcat留个接口,想扩展功能,直接实现这个接口,并利用多态,用父类指向子类就可以了。但是程序不是你客户想改就能改的,不能改动人家的程序,在人家的程序里new你的对象。所以就可以借助java的反射机制,只需事先写好你的类,让这个类实现Servlet接口,在配置文件里,把你要用的类名字想加载到里面(写到里面),应用程序(服务器)就可以加载你这个类的class文件,能够获得并调用你这个类的所有特性包括成员,方法。

2、反射机制的原理是通过字节码文件对象来获取某一个具体类的内部信息。就像我们想拿到或者修改小强的名字和年龄,只需通过Person类调用里面的方法就可以实现。

3、反射机制之获取Class对象的三种方式


package cn.itcast.reflect.demo;import cn.itcast.bean.Person;/* * JAVA反射机制是在运行状态中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法; * 对于任意一个对象,都能够调用它的任意一个方法和属性; * 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。  *  *  * 动态获取类中信息,就是java反射 。 * 可以理解为对类的解剖。 *  *  * 要想要对字节码文件进行解剖,必须要有字节码文件对象. * 如何获取字节码文件对象呢? *  */public class ReflectDemo {/** * @param args * @throws ClassNotFoundException  */public static void main(String[] args) throws ClassNotFoundException {getClassObject_3();}/* * 方式三: * 只要通过给定的类的 字符串名称就可以获取该类,更为扩展。 * 可是用Class类中的方法完成。 * 该方法就是forName. * 这种方式只要有名称即可,更为方便,扩展性更强 *方式三不需要Person类new对象,也不需要Person类调用静态方法,只要传进来一个类名字的字符串就ok, *但注意要把类前面的包名一齐写全才行啊!  */public static void getClassObject_3() throws ClassNotFoundException {String className = "cn.itcast.bean.Person";//就是这里,写类名字的拾获要写全啊!Class clazz = Class.forName(className);System.out.println(clazz);}/* * 方式二: * 2,任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。 * 相对简单,但是还是要明确用到类中的静态成员。 * 还是不够扩展。  *  */public static void getClassObject_2() {Class clazz = Person.class;Class clazz1 = Person.class;System.out.println(clazz==clazz1);}/* * 获取字节码对象的方式: * 1,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);}}
4、反射机制---获取Class中的构造函数

类没有初始化的意思就是这个类还没有被加载到内存中去。

而当被加载过来,再调用这个方法就是创建一个这个类的实体对象。


任何数据类型都可以被class所描述,故有String.class,int.class

构造器(构造函数)也是对象啊

package cn.itcast.reflect.demo;import java.io.FileReader;import java.lang.reflect.Constructor;public class ReflectDemo2 {/** * @param args * @throws Exception  * @throws InstantiationException  * @throws ClassNotFoundException  */public static void main(String[] args) throws ClassNotFoundException, InstantiationException, Exception {createNewObject_2();}public static void createNewObject_2() throws Exception {//cn.itcast.bean.Person p = new cn.itcast.bean.Person("小强",39);/* * 当获取指定名称对应类中的所体现的对象时, * 而该对象初始化不使用空参数构造该怎么办呢? * 既然是通过指定的构造 函数进行对象的初始化, * 所以应该先获取到该构造函数。 通过字节码文件对象即可完成。 * 该方法是:getConstructor(paramterTypes); *  */String name = "cn.itcast.bean.Person";//找寻该名称类文件,并加载进内存,并产生Class对象。Class clazz = Class.forName(name);//获取到了指定的构造函数对  象。Constructor constructor = clazz.getConstructor(String.class,int.class);//通过该构造器对象的newInstance方法进行对象的初始化。Object obj = constructor.newInstance("小明",38);}public static void createNewObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{//早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,//并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象.//cn.itcast.bean.Person p = new cn.itcast.bean.Person();//现在:String name = "cn.itcast.bean.Person";//找寻该名称类文件,并加载进内存,并产生Class对象。Class clazz = Class.forName(name);//如何产生该类的对象呢?Object obj  = clazz.newInstance();}}

采用反射机制的好处就在于,之前你要创建一个Person对象,就必须在人家的代码里手动写下Person p =new Person(),但是别人的代码是不允许你修改添加的。所以就体现了反射机制的好处,只需把字符串形式的类名"cn.itcast.bean.Person"(以及对象初始化信息,小强,38)把这些信息写在配置文件中,服务器通过流读取这些信息,就可以在服务器的程序里自动创建出Person类对象。如果服务器要创建的Person对象不需要初始化个人信息,即调用Person()构造函数的话,那就不用上图这么麻烦,直接如下图


初始化的意思:

拿一个程序来说就是先加载基本的数据拿变量来说就是给它 一个初始值

5、获取Class中的字段

上面我们讲了通过Class对象获得特定类的构造函数,当然我们还可以获得那个特定类的成员,当那个成员是私有的,可以获得但不能访问或修改。但是field.setAccessible(true);可以去掉私有权限,这样就可以访问或者修改那个成员变量的值了。Object o = field.get(obj);之所以这样写,是因为你传进去的是字符串形式的成员变量“age”,程序不知道age“是int类型的数据,所以就用了万能的Object来接收。就算是“name”返回的是String类型,Object也搞得定。


这里说明了程序有2部分组成:数据和数据结构

数据有很多类型:基本数据类型和引用数据类型,对象是引用数据类型的一种。而Object是所有数据类型的父类(包括对象数据类型,也包括基本数据类型)


6、获取Class中的类的方法

Method[] methods  = clazz.getMethods();//获取的是那个类全部的公有方法。(包含它的父类里面的公有方法像Object类的公有方法,但是无法获得它的私有方法)

methods = clazz.getDeclaredMethods();//只获取本类中所有方法,包含私有。 (但是不能获得它从父类那里继承过来的方法)


 下面具体举例来说明:反射机制的很强的扩展性,你不需要修改main函数的程序(不需要再main函数里面new你要扩展功能所用类的对象),只需从配置文件里加载上配置信息,从配置信息里面读取你要扩展功能所用类的字符串形式的类名字就可以在main函数中得到这个类的全部东西,想new这个类的对象就new对象,想用它的方法就用它的方法,主程序代码一个字不用改,只需修改下配置文件,就可以扩展功能。

下面以在主板上扩展声卡和网卡功能为例:

首先定义一个接口PCI(类似以前讲的USB接口),方便声卡,网卡实现后,可以接入主板。其实PCI就是Personal Computer Interface的缩写,也可以用来扩展显卡等等。

package cn.itcast.reflect.test;public interface PCI {public void open();public void close();}

接着定义(带有能扩展功能的主板类MainBoard

package cn.itcast.reflect.test;public class Mainboard {public void run() {System.out.println("main board run....");}public void usePCI(PCI p) {//PCI p = new SouncCard();if (p != null) {p.open();p.close();}}}

运行主程序类似于操作系统,ReflectTest



扩展的声卡程序如下:SoundCard


扩展网卡也是同样功能:NetCard

1、正则表达式
就是正确的规则,用来操作字符串数据,一个简单的表达式就代表了很多代码。


对于字符串的判断,传统方法也许需要很长,但是正则表达式只需一句话就能搞定,第一位是1-9,第二位在0-9,后面{4-14}为是重复第二位的形式出现。
ao+b,即字符串的第一个字母是a,最后一个字母是b,中间出现了o且不止1次。
ao{4,}b,即第一个字母为a,最后一个字母为b,中间o出现了5次及以上。
本来可以用\d来表示[0-9],但是在字符串中\通常带转义的意思"\d"代表转义了的d,而我们要的就是\d来表示[0-9],而不是转了义的,所以需要写成\\d的形式才可以表示出来。


.在正则匹配中有特殊含义,代表任意符号。要想去掉那个特殊含义就必须对它进行\.代表普通点,但是\有特殊含义,又需要在其前面加一个\,也就是\\.才代表普通的点.的意思。
3、切割
切割要引入组的概念,简单的来说几个左括号就是几个组。

分割这里要以tttt和mmmmm来当做分割符,只要是这种类型的都可以当做分隔符,规律就是第一个字符任意,所以为.后面的复用(重复)第一个,用(.)来表示组的概念,tttt的第二位也是t和第一组是一样的,直接拿第一组的标号来表示就ok,(.)\\1,\\1表示组标号,还有第三个,第四个,所以后面还有+,合起来就是(.)\\1+

<pre name="code" class="java">package cn.itcast.regex.demo;import java.util.regex.Matcher;import java.util.regex.Pattern;public class RegexDemo2 {/** * @param args */public static void main(String[] args) {/* * 正则表达式对字符串的常见操作: * 1, 匹配。 * 其实使用的就是String类中的matches方法。 *  * 2,切割。 * 其实使用的就是String类中的split方法。  *  * 3,替换。 * 其实使用的就是String类中的replaceAll()方法。 *  * 4,获取。 *  */functionDemo_4();}/* * 获取  * 将正则规则进行对象的封装。  * Pattern p = Pattern.compile("a*b"); *  //通过正则对象的matcher方法字符串相关联。获取要对字符串操作的匹配器对象Matcher .  * Matcher m = p.matcher("aaaaab");  * //通过Matcher匹配器对象的方法对字符串进行操作。  * boolean b = m.matches(); *  *  */public  static void functionDemo_4() {String str = "da jia hao,ming tian bu fang jia!";String regex = "\\b[a-z]{3}\\b";//1,将正则封装成对象。Pattern p = Pattern.compile(regex);//2, 通过正则对象获取匹配器对象。 Matcher m = p.matcher(str);//使用Matcher对象的方法对字符串进行操作。//既然要获取三个字母组成的单词 //查找。 find();System.out.println(str);while(m.find()){System.out.println(m.group());//获取匹配的子序列System.out.println(m.start()+":"+m.end());}}/* * 替换  */public static void functionDemo_3() {String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";str = str.replaceAll("(.)\\1+", "$1");System.out.println(str);String tel = "15800001111";//158****1111;tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");System.out.println(tel);}/* * 切割。 *  * 组:((A)(B(C)))  */public static void functionDemo_2(){String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";String[] names = str.split("(.)\\1+");//str.split("\\.");for(String name : names){System.out.println(name);}}/* * 演示匹配。  */public static void functionDemo_1(){//匹配手机号码是否正确。 String tel = "15800001111";String regex = "1[358]\\d{9}";   boolean b = tel.matches(regex);System.out.println(tel+":"+b);}}

5、正则表达式的替换功能
字符串也有替换的功能:


如果想把4个t变成1个t,即第二个参数想用第一个参数来匹配,可以用美元符号$

字符串函数repalceAll(),就是专门使用正则表达式的函数,里面的第一个参数就是正则表达式

6、Pattern类就是把正则表达式封装成对象。
描述正则就Pattern这么一个对象,字符串那些能调用正则表达式的函数内部其实都是用到了Pattern函数。  
只要是连续的3个字母就取出来,正则表达式这样写是不对的。要限定长度刚好为3的才可以。

记得拿.来切的时候要转义,用\\.

9、邮箱的校验


String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3})+";这一句意思是说

[a-zA-Z0-9_]:大写或者小写字母或者数字或者下划线_,+:有一位或者更多,有一个@符号,[a-zA-Z0-9]:大写或者小写字母或者数字,(\\.[a-zA-Z]{1,3}):一个.大写或者小写字母1到3位,例如   .com

package cn.itcast.regex.test;import java.util.TreeSet;public class RegexTest {/** * @param args */public static void main(String[] args) {/* * 1,治疗口吃:我我...我我...我我我要...要要要要...要要要要..学学学学学...学学编编...编编编编..编..程程...程程...程程程 * 2,对ip地址排序。  * 3,对邮件地址校验。  */test_3();}//对邮件地址校验。public static void test_3() {String mail = "abc1@sina.com.cn";String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3})+";regex = "\\w+@\\w+(\\.\\w+)+";//1@1.1boolean b = mail.matches(regex);System.out.println(mail+":"+b);}/* * 1,治口吃。 */public static void test_1(){String str = "我我...我我...我我我要...要要要要...要要要要..学学学学学...学学编编...编编编编..编..程程...程程...程程程";//1,将字符串中.去掉。 用替换。str = str.replaceAll("\\.+", "");System.out.println(str);//2,替换叠词。str = str.replaceAll("(.)\\1+", "$1");System.out.println(str);}/* * ip地址排序。  *  * 192.168.10.34 127.0.0.1 3.3.3.3  105.70.11.55 */public static void test_2(){String ip_str = "192.168.10.34  127.0.0.1  3.3.3.3  105.70.11.55";//1,为了让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);//1,将ip地址切出。String[] ips = ip_str.split(" +");TreeSet<String> ts = new TreeSet<String>();for(String  ip : ips){//System.out.println(ip);ts.add(ip);}for(String ip : ts){System.out.println(ip.replaceAll("0*(\\d+)", "$1"));}}}
10、正则表达式之爬虫

网页爬虫:其实就一个程序用于在互联网中获取符合指定规则的数据。 

下面就是分别爬取本地网页和互联网上网页的邮箱数据


<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: normal;"></span></span>
0 0
原创粉丝点击