黑马程序员java学习笔记——正则表达式、反射
? ? 正則表達(dá)式
? ? 正則表達(dá)式是一個(gè)用來(lái)描述或者匹配一系列符合某個(gè)句法規(guī)則的字符串的表達(dá)式,也就是符合一定規(guī)則的表達(dá)式,操作字符串有很多方法,但是這些方法操作起來(lái)太簡(jiǎn)單,組合起來(lái)操作復(fù)雜數(shù)據(jù),代碼過(guò)多,而對(duì)字符串進(jìn)行操作既便捷又簡(jiǎn)單的方式就是正則表達(dá)式。
? ? 作用:用于操作字符串。
? ? 特點(diǎn):用特定符號(hào)表示一些代碼操作,這樣可以簡(jiǎn)化書(shū)寫(xiě)。
? ? 好處:可以簡(jiǎn)化對(duì)字符串的復(fù)雜操作。
? ? 弊端:正則越長(zhǎng),閱讀性越差。
? ? 正則表達(dá)式的構(gòu)造摘要
? ??字符類(lèi)?
? ? [abc] ? ? ? ? a、b或c(簡(jiǎn)單類(lèi))?
? ? [^abc] ? ? ? ?任何字符,除了a、b或c(否定)?
? ? [a-zA-Z] ? ? ?a 到z或A到Z,兩頭的字母包括在內(nèi)(范圍)?
? ? [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](減去)?
??
? ? 預(yù)定義字符類(lèi)?
? ? . ? ? ? ?任何字符(與行結(jié)束符可能匹配也可能不匹配)?
? ? \d ? ? ? 數(shù)字:[0-9]?
? ? \D ? ? ? 非數(shù)字: [^0-9]?
? ? \s ? ? ? 空白字符:[ \t\n\x0B\f\r]?
? ? \S ? ? ? 非空白字符:[^\s]?
? ? \w ? ? ? 單詞字符:[a-zA-Z_0-9]?
? ? \W ? ? ? 非單詞字符:[^\w]?
? ? 邊界匹配器?
? ? ^ ? ? ? ?行的開(kāi)頭?
? ? $ ? ? ? ?行的結(jié)尾?
? ? \b ? ? ? 單詞邊界?
? ? \B ? ? ? 非單詞邊界?
? ? \A ? ? ? 輸入的開(kāi)頭?
? ? \G ? ? ? 上一個(gè)匹配的結(jié)尾?
? ? \Z ? ? ? 輸入的結(jié)尾,僅用于最后的結(jié)束符(如果有的話)?
? ? \z ? ? ? 輸入的結(jié)尾?
??
? ? Greedy 數(shù)量詞?
? ? X? ? ? ? X,一次或一次也沒(méi)有?
? ? X* ? ? ? X,零次或多次?
? ? X+ ? ? ? X,一次或多次?
? ? X{n} ? ? X,恰好n次?
? ? X{n,} ? ?X,至少n次?
? ? X{n,m} ? X,至少n次,但是不超過(guò)m次?
? ? 組:當(dāng)你想要對(duì)一個(gè)規(guī)則的結(jié)果進(jìn)行重用時(shí),可以將其封裝成組,這個(gè)組里的結(jié)果就可以被再次使用,用()來(lái)完成,組封裝完以后會(huì)有一個(gè)自動(dòng)的編號(hào),從1開(kāi)始,想要使用已有的組可以通過(guò)\n(n就是組的編號(hào))的形式來(lái)獲取,如"(.)//1",這就表示后一位的結(jié)果和前一位的結(jié)果一樣,如果這個(gè)位置的數(shù)出現(xiàn)多次,可表示為"(.)//1+",租和matches不同的是matches匹配到不符合規(guī)則的就不再匹配,而組會(huì)繼續(xù)往下判斷,像這樣((())())出現(xiàn)多個(gè)組時(shí),我們要判斷有幾個(gè)組,就要看有幾個(gè)左括號(hào),有幾個(gè)就有幾個(gè)組,判斷到第幾個(gè)左括號(hào),它就是第幾個(gè)組。
? ? 其實(shí)學(xué)習(xí)正則表達(dá)式就是學(xué)習(xí)一些特殊符號(hào)。
? ? 具體操作
? ? 匹配
? ? matches(regex):返回的是一個(gè)boolean型的值,用規(guī)則匹配整個(gè)字符串,只要有一處匹配不符合規(guī)則,就不再向下匹配。
? ? 要注意的是,在正則表達(dá)式中,反斜杠“\”都需要成對(duì)出現(xiàn)的“\\”,因?yàn)樵谧址小癨”是轉(zhuǎn)義字符。
? ? 練習(xí):對(duì)QQ號(hào)碼進(jìn)行校驗(yàn),5~15位,0不能開(kāi)頭,只能是數(shù)字。
class RegexDemo {public static void main(String[] args) {String qq = "045364768";checkQQ(qq);}public static void checkQQ(String qq){String regex = "[1-9]\\d{4,14}";//定義匹配規(guī)則;boolean b = qq.matches(regex);if(b)System.out.println("qq格式正確");elseSystem.out.println("qq格式錯(cuò)誤");} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 練習(xí):匹配手機(jī)號(hào)段只有:13xxx、15xxx、18xxxx。
class RegexDemo {public static void main(String[] args) {String num = "13253464768";checkQQ(num);}public static void checkQQ(String tel){String regex = "1[358]\\d{9}";boolean b = tel.matches(regex);if(b)System.out.println("號(hào)碼格式正確");elseSystem.out.println("號(hào)碼格式錯(cuò)誤");} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 切割
? ? split(String regex):返回的是一個(gè)字符串?dāng)?shù)組。
/* 需求:按照疊詞對(duì)一個(gè)字符串進(jìn)行切割。 */ class RegexDemo {public static void main(String[] args) {String str = "adaawsfffafgwdfbbbbasdd";String regex = "(.)\\1+";splitDemo(str,regex);}public static void splitDemo(String str,String regex){String[] arr = str.split(regex);//對(duì)字符串按照指定規(guī)則進(jìn)行切割;for(String s : arr){System.out.println(s);}} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 注意:在正則表達(dá)式中不能直接用“.”去對(duì)字符串進(jìn)行切割,以為“.”是正則表達(dá)式中的一個(gè)特殊符號(hào),代表的是任意字符,應(yīng)該寫(xiě)成“\\.”,同理獲取盤(pán)符應(yīng)使用“\\\\”;對(duì)于疊詞我們可以使用組,并對(duì)組進(jìn)行捕獲,用\n的形式表示。
? ? 替換
? ? replaceAll(String regex,String replacement):返回的是字符串。
? ? 特殊部分
? ? 如:將疊詞替換成“&”符號(hào).
? ? 將重疊的字母替換成單個(gè)的該字母:在替換的時(shí)候我們可以用一個(gè)特殊符號(hào)來(lái)代表這個(gè)組“$1”,意思就是拿前一個(gè)規(guī)則的第一個(gè)組的那個(gè)字母("(.)\\1+","$1")。
/* 需求:按照疊詞對(duì)一個(gè)字符串進(jìn)行切割。 */ class RegexDemo {public static void main(String[] args) {String str = "adaa142424wsfffaf4243gwdfb234234234234bbba";String regex = "\\d{5,}";String newstr = "*";String str1 = "sdfggdfdfgdddfdffff";String regex1 = "(.)\\1+";removeAllDemo(str,regex,newstr);removeAllDemo(str1,regex1,"$1");}public static void removeAllDemo(String str,String regex,String newstr){String s = str.replaceAll(regex,newstr);System.out.println(s);} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 獲取
? ? 獲取就是按照指定規(guī)則取出字符串子串。
? ? 匹配返回的是真假,替換返回的是替換后的字符串,切割是把規(guī)則以外的取出來(lái),而獲取是要把符合規(guī)則的取出來(lái)。
? ? 步驟:1,將正則表達(dá)式封裝成對(duì)象。
? ? ? ? ? 2,讓正則對(duì)象和要操作的字符串相關(guān)聯(lián)。
? ? ? ? ? 3,關(guān)聯(lián)后,獲取正則匹配引擎。
? ? ? ? ? 4,通過(guò)引擎對(duì)符合規(guī)則的子串進(jìn)行操作,比如取出。
? ? Pattern類(lèi):正則表達(dá)式的編譯表示形式,它可以描述正則表達(dá)式,對(duì)正則表達(dá)式進(jìn)行封裝,它在java.util.regex包中。
? ? compile(regex):該方法是靜態(tài)的,返回一個(gè)Pattern對(duì)象,把正則表達(dá)式傳遞給這個(gè)方法,它就會(huì)把該正則表達(dá)式封裝成一個(gè)Pattern對(duì)象返回。
? ? matcher(CharSequence input?):讓正則對(duì)象和要操作的字符串相關(guān)聯(lián),返回的是Matcher匹配器。
? ? Matcher類(lèi)
? ? matches():其實(shí)String類(lèi)中的matches方法就是用的Pattern和Matcher這兩個(gè)類(lèi)的對(duì)象來(lái)完成的,只不過(guò)被String的方法封裝后,用起來(lái)較為簡(jiǎn)單,但是功能卻單一。
? ? find():查找與該模式匹配的輸入序列的下一個(gè)子序列,就是將規(guī)則作用到字符串上,并進(jìn)行符合規(guī)則的子串查找,返回的是一個(gè)boolean型的值;
? ? group():返回由以前匹配操作所匹配的輸入子序列,就是用于獲取匹配后的結(jié)果。
? ? 這兩個(gè)方法要結(jié)合使用,功能類(lèi)似迭代器的hasNext方法和next方法,判斷有沒(méi)有下一個(gè),有才取。?
? ? end():返回最后匹配字符之后的偏移量;
? ? start():返回以前匹配的初始索引;??
? ? 邊界匹配器:\b—單詞邊界。
? ? 演示:
import java.util.regex.*;; class RegexDemo {public static void main(String[] args) {getDemo();}public static void getDemo(){String str = "ming tian jiu yao fang jia le ,da jia。";System.out.println(str);String reg = "\\b[a-z]{4}\\b";//將規(guī)則封裝成對(duì)象。Pattern p = Pattern.compile(reg);//讓正則對(duì)象和要作用的字符串相關(guān)聯(lián)。獲取匹配器對(duì)象。Matcher m = p.matcher(str);while(m.find())//將規(guī)則作用到字符串上,并進(jìn)行符合規(guī)則的子串查找;{System.out.println(m.group());//用于獲取匹配后結(jié)果;System.out.println(m.start()+"...."+m.end());}} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 在我們使用正則表達(dá)式時(shí)應(yīng)該怎么選擇呢?
? ? 思路方式:
? ? 1,如果只想知道該字符是否對(duì)是錯(cuò),使用匹配。
? ? 2,想要將已有的字符串變成另一個(gè)字符串,替換。
? ? 3,想要按照自定的方式將字符串變成多個(gè)字符串。切割。獲取規(guī)則以外的子串。
? ? 4,想要拿到符合需求的字符串子串,獲取。獲取符合規(guī)則的子串。? ?
? ? 練習(xí)1:將字符串轉(zhuǎn)換成"我要學(xué)編程"。
/* 需求: 將下列字符串轉(zhuǎn)成:我要學(xué)編程。 "我我...我我...我要..要要...要要...學(xué)學(xué)學(xué)....學(xué)學(xué)...編編編...編程..程.程程...程...程"; 思路:將已有字符串變成另一個(gè)字符串。使用替換功能。 1,可以先將 . 去掉。 2,在將多個(gè)重復(fù)的內(nèi)容變成單個(gè)內(nèi)容。 */ import java.util.regex.*;; class RegexDemo {public static void main(String[] args) {test();}public static void test(){String str = "我我...我我...我要..要要...要要...學(xué)學(xué)學(xué)....學(xué)學(xué)...編編編...編程..程.程程...程...程";str = str.replaceAll("\\.+","");//去掉".";str = str.replaceAll("(.)\\1+","$1");//將疊詞轉(zhuǎn)換成單詞;System.out.println(str);} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 練習(xí)2:將ip地址進(jìn)行地址段順序的排序。
/* 需求: 192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30 將ip地址進(jìn)行地址段順序的排序。 思路:還按照字符串自然順序,只要讓它們每一段都是3位即可。 1,按照每一段需要的最多的0進(jìn)行補(bǔ)齊,那么每一段就會(huì)至少保證有3位。 2,將每一段只保留3位。這樣,所有的ip地址都是每一段3位。 */ import java.util.*; import java.util.regex.*;; class RegexDemo {public static void main(String[] args) {ipSort();}public static void ipSort(){String ip = "192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30";ip = ip.replaceAll("(\\d+)","00$1");//在每一段地址前補(bǔ)兩個(gè)0,保證每一段至少3位;ip = ip.replaceAll("0*(\\d{3})","$1");//每段只保留3位,將多余的0去掉;String[] arr = ip.split(" ");//用空格對(duì)地址串進(jìn)行切割;TreeSet<String> ts = new TreeSet<String>();for(String s : arr){ts.add(s);//將切割后的子串作為元素存到TreeSet集合中,進(jìn)行排序;}for(String s : ts){System.out.println(s.replaceAll("0*(\\d+)","$1"));//把每個(gè)地址段前的0去掉;}}}
? ? 運(yùn)行結(jié)果:
? ??
? ? 練習(xí)3:對(duì)郵件地址進(jìn)行校驗(yàn)
/* 需求: 對(duì)郵件地址進(jìn)行校驗(yàn)。 */ import java.util.regex.*;; class RegexDemo {public static void main(String[] args) {checkMail();}public static void checkMail(){String mail = "abc12@sina.com";String reg = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";//較為精確的匹配。boolean flag = mail.matches(reg);if(flag)System.out.println("地址輸入正確");elseSystem.out.println("地址輸入錯(cuò)誤,請(qǐng)重輸");} }
? ? 運(yùn)行結(jié)果:
? ??
? ? 網(wǎng)頁(yè)爬蟲(chóng)
/* 網(wǎng)頁(yè)爬蟲(chóng)(蜘蛛) */ import java.io.*; import java.util.regex.*; import java.net.*; import java.util.*; class RegexDemo {public static void main(String[] args) throws Exception{getMails_1();}/*獲取指定網(wǎng)頁(yè)中的郵件地址。使用獲取功能。Pattern Matcher*/public static void getMails_1()throws Exception{URL url = new URL("http://www.doyo.cn/game/luntan/viewthread/229522");//將地址封裝成對(duì)象;URLConnection conn = url.openConnection();//和服務(wù)器建立連接;BufferedReader bufIn = new BufferedReader(new InputStreamReader(conn.getInputStream()));//獲取網(wǎng)絡(luò)讀取流;String line = null;String mailreg = "\\w+@\\w+(\\.\\w+)+";//定義規(guī)則;Pattern p = Pattern.compile(mailreg);//將規(guī)則進(jìn)行封裝;while((line=bufIn.readLine())!=null){Matcher m = p.matcher(line);//和讀到的每一行字符串進(jìn)行關(guān)聯(lián);while(m.find())//查找匹配規(guī)則的郵箱;{System.out.println(m.group());//獲取匹配的郵箱;}}}/*獲取指定文檔中的郵件地址。使用獲取功能。Pattern Matcher*/public static void getMails_2()throws Exception{BufferedReader bufr = new BufferedReader(new FileReader("mail.txt"));//讀取文件String line = null;String mailreg = "\\w+@\\w+(\\.\\w+)+";//定義規(guī)則;Pattern p = Pattern.compile(mailreg);//將規(guī)則進(jìn)行封裝;while((line=bufr.readLine())!=null){Matcher m = p.matcher(line);//和讀到的每一行字符串進(jìn)行關(guān)聯(lián);while(m.find())//查找匹配規(guī)則的郵箱;{System.out.println(m.group());//獲取匹配的郵箱;}}} }
? ? 運(yùn)行結(jié)果:
? ? ??
? ? 反射技術(shù)
? ? 概述
? ? java類(lèi)用于描述一類(lèi)事物的共性,該類(lèi)事物有什么屬性,沒(méi)有什么屬性,這個(gè)屬性值是什么,則是由這個(gè)類(lèi)的實(shí)例對(duì)象來(lái)確定的,不同的實(shí)例有不同的值,java程序中各個(gè)java類(lèi),它們是否也屬于同一事物?是否可以用一個(gè)類(lèi)來(lái)描述這類(lèi)事物?答案是肯定的,這個(gè)類(lèi)的名字就是:Class,我們可以通過(guò)這個(gè)類(lèi)動(dòng)態(tài)的獲取其他類(lèi)中的信息,包括屬性和方法等,其實(shí),反射就是把java中的各種成分映射成相應(yīng)的java類(lèi),相當(dāng)于把一個(gè)類(lèi)進(jìn)行拆解,然后將拆解后的每個(gè)部分拿出來(lái),單獨(dú)封裝。
? ? 那么Class類(lèi)中包含了哪些信息呢?
? ? Class類(lèi)中包含類(lèi)的名字,類(lèi)的訪問(wèn)屬性,類(lèi)屬于哪一個(gè)包,字段名稱(chēng)列表,方法名稱(chēng)列表等,學(xué)習(xí)反射,首先就要明白Class這個(gè)類(lèi)。
? ? Class這個(gè)類(lèi)對(duì)應(yīng)的是字節(jié)碼文件對(duì)象,每一份字節(jié)碼都是一個(gè)Class實(shí)例對(duì)象,獲取字節(jié)碼文件對(duì)象的方式有三種。
? ? 1,類(lèi)名.class,如:System.Class。
? ? 2,對(duì)象.getClass(),如:new Data().getClass();
? ? 3,Class.forName(),這是個(gè)靜態(tài)方法,如:Class.forName("java.lang.String"),這里要將完整的類(lèi)名作為參數(shù)傳遞進(jìn)去。
? ? forName這個(gè)方法得到類(lèi)的字節(jié)碼有兩種情況,一種是這個(gè)類(lèi)的字節(jié)碼已經(jīng)加載到內(nèi)存中,這是就不需要在加載,直接獲取,另一種就是在獲取這份字節(jié)碼時(shí),還沒(méi)有加載到內(nèi)存中,這時(shí)需要把類(lèi)加載進(jìn)來(lái),然后把加載進(jìn)來(lái)字節(jié)碼在內(nèi)存中緩存起來(lái),然后通過(guò)這個(gè)方法返回。
? ? 在java中基本數(shù)據(jù)類(lèi)型也有對(duì)應(yīng)的Class對(duì)象,void也有,如果我們要表示基本數(shù)據(jù)類(lèi)型的字節(jié)碼文件對(duì)象,可以用int.class也可以用Integer.TYPE。
? ? 總之,只要是在源程序中出現(xiàn)的類(lèi)型,都有各自的Class實(shí)例對(duì)象。
? ? Class類(lèi)為我們提供了很多方法來(lái)獲取相應(yīng)的類(lèi)中變量,構(gòu)造方法,方法等,下面我們來(lái)看這些成分對(duì)應(yīng)的類(lèi)在反射中是怎么使用的。
? ? Constructor--構(gòu)造方法的反射應(yīng)用
? ? getConstructors():這個(gè)方法返回的這個(gè)類(lèi)中的所有構(gòu)造方法;
? ? getConstructor(Class<?>...?parameterTypes):返回某一個(gè)構(gòu)造方法。
? ? 如:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
? ? Constructor對(duì)應(yīng)的是構(gòu)造方法這個(gè)類(lèi),而它的對(duì)象對(duì)應(yīng)的是某一個(gè)構(gòu)造方法,上面的constructor對(duì)應(yīng)的就是字符串的String(StringBuffer buffer)這個(gè)構(gòu)造函數(shù)。
? ? 現(xiàn)在我們得到了String類(lèi)的構(gòu)造函數(shù),那么怎么通過(guò)反射的方式用這個(gè)構(gòu)造函數(shù)去創(chuàng)建實(shí)例對(duì)象呢?
? ? 通常方式:String str = new String(new StringBuffer("abc"));
? ? 在Constructor類(lèi)中,有專(zhuān)門(mén)獲取實(shí)例的方法,這個(gè)方法就是newInstance
? ? 反射方式:String str = (String)constructor.newInstance(new StringBuffer("adc"));注意這里是需要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換的,虛擬機(jī)只知道我們?cè)谑褂胣ewInstance獲取構(gòu)造方法,但是不知道是獲取那個(gè)對(duì)象的構(gòu)造方法,編譯時(shí),只對(duì)語(yǔ)法進(jìn)行檢查,并不去執(zhí)行語(yǔ)句,沒(méi)執(zhí)行就不知道,也就是說(shuō)編譯器只看變量的定義,不看代碼的執(zhí)行。
? ? 注意:獲取方法時(shí),要用到類(lèi)型,調(diào)用獲得的方法時(shí),要用到與之相同的類(lèi)型的對(duì)象。
? ? 通過(guò)反射創(chuàng)建實(shí)例對(duì)象要經(jīng)過(guò)Class對(duì)象獲取constructor,再由constructor獲取實(shí)例對(duì)象,在Class類(lèi)中有一個(gè)newInstance方法,這個(gè)方法先得到默認(rèn)的構(gòu)造方法,然后用該構(gòu)造方法創(chuàng)建實(shí)例對(duì)象。
? ? 演示:
import java.lang.reflect.*; class ReflectDemo {public static void main(String[] args)throws Exception{Class clazz = Class.forName("java.lang.String");//獲取String類(lèi)的Class對(duì)象;Constructor constructor = clazz.getConstructor(String.class);//獲取String類(lèi)的傳入String參數(shù)的構(gòu)造函數(shù);String str = (String)constructor.newInstance("asfsdfsa");//創(chuàng)建String類(lèi)對(duì)象;System.out.println(str.indexOf('s'));} }
? ? Filed類(lèi)--成員變量的反射
? ? 對(duì)于一個(gè)類(lèi)中的成員變量,在反射中,我們用Filed類(lèi)來(lái)描述。
? ? 方法
? ? getField():獲取變量所屬的類(lèi),返回的是一個(gè)Class對(duì)象,參數(shù)(String name)。
? ? 演示:
import java.lang.reflect.*; class Demo {private int x;public int y;Demo(int x,int y){this.x = x;this.y = y;} } class ReflectDemo {public static void main(String[] args)throws Exception{Demo d = new Demo(4,6);Field fieldY = d.getClass().getField("y");/*這得到的是Class對(duì)象,每個(gè)Demo對(duì)象身上都有一個(gè)fieldY,它在d上是6,在其它對(duì)象上可能就不是,fieldY不代表一個(gè)具體的值,只代表一個(gè)變量,不代表某個(gè)對(duì)象具體的值,fieldY它不是對(duì)象身上的變量,而是類(lèi)上的,要用它去取某個(gè)對(duì)象的值,就要用到get(該類(lèi)的一個(gè)對(duì)象)方法;*/System.out.println(fieldY.get(d));//這就是get方法,得到d這個(gè)對(duì)象y的值是6;Field fieldX = d.getClass().getDeclaredField("x");//get方法只能得到可見(jiàn)的變量,不可見(jiàn)的要用getDeclaredField這個(gè)方法;fieldX.setAccessible(true);//此個(gè)方法可以將這個(gè)變量設(shè)置為可見(jiàn),這就是暴力反射;System.out.println(fieldX.get(d));} }
? ? 成員變量綜合案例:將任意一個(gè)對(duì)象中的所有String類(lèi)型的成員變量所對(duì)應(yīng)的字符串內(nèi)容“b”變成“a”。
? ? getFields():獲取一個(gè)類(lèi)中的所有成員變量,返回的是一個(gè)Field類(lèi)型的數(shù)組。
? ? 在Field中的方法:
? ? get(Object obj):返回的是Object對(duì)象,返回這個(gè)成員變量的值;
? ? set(Object obj,Object value):設(shè)置某個(gè)成員變量的值;
? ? getType():獲取成員變量的類(lèi)型對(duì)應(yīng)的字節(jié)碼文件對(duì)象,返回的是一個(gè)Class對(duì)象。
? ? 演示案例:
import java.lang.reflect.*; class Demo {public String str1 = "basketball";public String str2 = "black";public String str3 = "field"; } class ReflectDemo {public static void main(String[] args)throws Exception{Demo d = new Demo();Field[] fields = d.getClass().getFields();//獲取Demo類(lèi)中所有的成員變量;for(Field field : fields){if(field.getType()==String.class)//比較字節(jié)碼用“==”,雖然也可以用equals,但語(yǔ)義不準(zhǔn)確,因?yàn)槭峭环葑止?jié)碼;{String oldValue = (String)field.get(d);//獲取變量的值;String newValue = oldValue.replace('b','d');//將這個(gè)變量中的b替換成d;field.set(d,newValue);//將對(duì)象中的原有的值替換成改變后的值;}}System.out.println(d.str1);System.out.println(d.str2);System.out.println(d.str3);} }
? ? Method類(lèi)--成員方法的反射
? ? Method類(lèi):類(lèi)里的方法,字節(jié)碼對(duì)象的方法,不是對(duì)象的方法。
? ? getMethod(方法名,這個(gè)方法中傳入的參數(shù)對(duì)應(yīng)的字節(jié)碼對(duì)象):返回一個(gè)Method對(duì)象,獲得字節(jié)碼中的某一個(gè)成員方法。
? ? 例如:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
? ? 調(diào)用方法
? ? 普通方式:str.charAt(4);
? ? 反射方式:charAt.invoke(str,4);
? ? invoke(對(duì)象,傳入的參數(shù)):對(duì)帶有指定參數(shù)的指定對(duì)象調(diào)用由此 Method 對(duì)象表示的底層方法。
? ? 也就是說(shuō)invoke這個(gè)方法是charAt這個(gè)方法對(duì)象身上的方法。
import java.lang.reflect.*; class ReflectDemo {public static void main(String[] args)throws Exception{String str1 = "afsdg";Method methodCharAt = str1.getClass().getMethod("charAt",int.class);//獲取方法對(duì)象;System.out.println(methodCharAt.invoke(str1,2));} }
? ? invoke(null,1)這個(gè)方法不需要對(duì)象,說(shuō)明方法是靜態(tài)的,也就是說(shuō)如果你想調(diào)用一個(gè)靜態(tài)方法,就在這里設(shè)置成空。
? ? 用反射執(zhí)行某個(gè)類(lèi)中的main方法
import java.lang.reflect.*; class ReflectDemo {public static void main(String[] args)throws Exception{//Demo.main(new String[]{"111","222","333"});//普通方法//反射方法String startClassName = args[0];//動(dòng)態(tài)的在這個(gè)main方法上指定要調(diào)用的main方法所屬的類(lèi);Method mainMethod = Class.forName(startClassName).getMethod("main",String[].class);//獲取要調(diào)用的main方法對(duì)象;mainMethod.invoke(null,new Object[]{new String[]{"aaa","bbb","ccc"}});//調(diào)用main方法。//或者mainMethod.invoke(null,(Object)new String[]{"111","222","333"});} } class Demo {public static void main(String[] args)throws Exception{for(String s : args){System.out.println(s);}} }
? ? 運(yùn)行結(jié)果:
? ? ? ??
? ? 為什么要用發(fā)射來(lái)執(zhí)行main方法?
? ? 在我們自己的主函數(shù)上是可以傳參數(shù)進(jìn)來(lái)的,如果你傳的第一個(gè)參數(shù)args[0]是一個(gè)類(lèi)的名字,我們可以通過(guò)反射的方式獲得這個(gè)類(lèi)中的main方法對(duì)象,然后調(diào)用這個(gè)main方法,這樣可以通過(guò)在我們的主函數(shù)的參數(shù)傳遞不同的類(lèi)名,動(dòng)態(tài)的獲取這些類(lèi)的主函數(shù)。
? ? 在invoke方法上,因?yàn)閙ain方法是靜態(tài)的,所以對(duì)象設(shè)置為null,直接被類(lèi)名調(diào)用,不需要對(duì)象,而把字符串轉(zhuǎn)成Object數(shù)組是因?yàn)樾掳姹疽嫒堇习姹?#xff0c;而老版本中中還沒(méi)有出現(xiàn)可變參數(shù),那時(shí)傳的還是數(shù)組,所以字符串?dāng)?shù)組會(huì)被拆開(kāi),變成了3個(gè)對(duì)象,這就造成參數(shù)傳遞的錯(cuò)誤,會(huì)在編譯時(shí)發(fā)生異常。
? ? 數(shù)組的反射類(lèi)型
? ? 每一個(gè)數(shù)組都屬于同一個(gè)Class,但這些數(shù)組要保證具有相同的元素類(lèi)型以及具有相同的維度。
? ? 如:int[] a1 = new int[3];
? ? ? ? int[] a2 = new int[4];
? ? ? ? int[][] a3 = new int[3][4];
? ? ? ? String[] a4 = new String[3];
? ? 其中a1和a2屬于同一個(gè)Class,a1和其他的都不是,因?yàn)楹蚢3維數(shù)不同,和a4元素類(lèi)型不同。
? ? 代表數(shù)組的Class實(shí)例對(duì)象的getSuperClass方法返回的父類(lèi)為Object類(lèi)對(duì)應(yīng)的Class。
? ? 基本類(lèi)型數(shù)組可以被當(dāng)作Object使用,但不能被當(dāng)作Object[]使用,非基本類(lèi)型的都可以。
? ? Arrays.asList()處理int[]和String[]的區(qū)別就在于得到的集合會(huì)把int[]整個(gè)數(shù)組當(dāng)成一個(gè)元素放進(jìn)集合,而String[]數(shù)組會(huì)把數(shù)組中的每一個(gè)字符串當(dāng)成元素存進(jìn)數(shù)組。
? ? 對(duì)數(shù)組進(jìn)行反射
? ? Array工具類(lèi)用于對(duì)數(shù)組的反射操作,它里邊的方法全是靜態(tài)的。
? ? 演示:
import java.lang.reflect.*; class ReflectDemo {public static void main(String[] args)throws Exception{int[] a1 = {4,3,2,5,3};String[] a2 = {"ada","adadsa","adad","fffgf"};printArray(a1);printArray(a2);}public static void printArray(Object obj){Class clazz = obj.getClass();//獲取數(shù)組的字節(jié)碼對(duì)象;if(clazz.isArray())//判斷這個(gè)字節(jié)碼對(duì)象是不是數(shù)組的;{int len = Array.getLength(obj);//獲取數(shù)組的長(zhǎng)度;for(int x=0;x<len;x++)//對(duì)數(shù)組進(jìn)行遍歷;{System.out.println(Array.get(obj,x));}}else{System.out.println(obj);}} }
? ? HashCode分析
? ? HashCode方法的作用是通過(guò)Hash算法來(lái)對(duì)對(duì)象進(jìn)行比較,在我們比較一個(gè)集合中的元素是否相同時(shí),一般是把這個(gè)對(duì)象和集合中的每一個(gè)元素進(jìn)行比較,這樣如果元素個(gè)數(shù)太多,比較起來(lái)效率就變的很低,所以,有人發(fā)明了哈希算法,通過(guò)這種算法算出的哈希值,我們把集合分成了很多區(qū)域,當(dāng)某個(gè)對(duì)象的哈希值符合某個(gè)區(qū)域的特點(diǎn)時(shí),就到這個(gè)區(qū)域去尋找是否存在與這個(gè)對(duì)象相同的元素,這樣就大大的縮小了比較范圍,增加了效率,但是這種方法只適用于比較原理基于哈希算法的集合。
? ? 內(nèi)存泄漏:當(dāng)一個(gè)對(duì)象被存儲(chǔ)今HashSet集合,就不能再修改參與HashCode值運(yùn)算的字段了,否則對(duì)象修改后的哈希值就與修改前存進(jìn)集合的哈希值不同了,這是如果要?jiǎng)h除集合中的這個(gè)對(duì)象,就找不到了,造成內(nèi)存泄漏。
? ? 反射技術(shù)開(kāi)發(fā)框架
? ? 框架相當(dāng)于開(kāi)發(fā)商蓋房子,蓋的時(shí)候,并不知道住進(jìn)來(lái)的人會(huì)怎么去裝修,比如說(shuō)門(mén)窗的安裝,這時(shí)候,開(kāi)發(fā)商只需要給住戶(hù)提供門(mén)窗的框架,后期住戶(hù)要使用什么樣的門(mén)窗有他們自己決定,如果我們自己一步步的去建房,走到裝修這一步要花費(fèi)很多功夫,如果已經(jīng)有了開(kāi)發(fā)商給我們提供的這個(gè)框架,就會(huì)變得簡(jiǎn)單。
? ? 其實(shí)java中的框架也是一樣的道理,我在寫(xiě)框架的時(shí)候,并不知道后來(lái)的人會(huì)給這個(gè)框架傳遞什么樣的類(lèi),所以在程序中也沒(méi)辦法直接創(chuàng)建這個(gè)類(lèi)的對(duì)象,要讓框架獲得這樣未卜先知的能力,我們?cè)陂_(kāi)發(fā)框架時(shí)就需要用到反射。
? ? 我們以后使用別人寫(xiě)的類(lèi)有兩種使用方式:一種是我去調(diào)用別人的類(lèi),另一種是別人調(diào)用我寫(xiě)的類(lèi),這兩種類(lèi)是有區(qū)別的,前一種叫做工具類(lèi),另一種就是框架,框架中使用的就是我們所寫(xiě)的類(lèi)。
? ? 下面我們來(lái)制作一個(gè)簡(jiǎn)單的框架:
import java.lang.reflect.*; import java.io.*; import java.util.*; class Demo {public int x;public int y;Demo(int x,int y){this.x = x;this.y = y;} } class ReflectDemo {public static void main(String[] args)throws Exception{Demo d1 = new Demo(4,6);Demo d2 = new Demo(4,5);Demo d3 = new Demo(4,6);InputStream in = new FileInputStream("config.properties");//用文件讀取流和配置文件相關(guān)聯(lián);Properties prop = new Properties();prop.load(in);//將流中的數(shù)據(jù)加載進(jìn)這個(gè)集合中;String className = prop.getProperty("className");//獲取鍵對(duì)應(yīng)的值;Class clazz = Class.forName(className);//獲取這個(gè)值對(duì)應(yīng)的class對(duì)象;Collection collection = (Collection)clazz.newInstance();//獲取這個(gè)字節(jié)碼的實(shí)例對(duì)象;collection.add(d1);//將數(shù)據(jù)添加進(jìn)集合中;collection.add(d2);collection.add(d3);System.out.println(collection.size());//獲取集合的大小;in.close();//關(guān)流;} }
總結(jié)
以上是生活随笔為你收集整理的黑马程序员java学习笔记——正则表达式、反射的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: pip安装第三方库 报错:You sho
- 下一篇: 柳下惠_拔剑-浆糊的传说_新浪博客