送給初學者的Java基礎面試題!!!
先給大家複習一下基礎題!!!
Java基礎是java初學者的起點,是幫助你從小白入門到精通必學基礎課程!
為初學者而著!
配套學習:
面向物件
什麼是面向物件?什麼是面向過程?
- 面向過程就是分析出實現需求所需要的步驟,通過函式一步一步實現這些步驟,接著依次呼叫即可。
- 面向物件是把整個需求按照特點、功能劃分,將這些存在共性的部分封裝成物件,建立物件不是為了完成某一個步驟,而是描述某個事物在解決問題的步驟中的行為
- 面向物件是將每一個步驟抽象為行為,便於複用和擴充套件
面向物件的三大基本特徵是什麼?
- 封裝就是隱藏物件的屬性和實現細節,封裝的目的是增強安全性和簡化程式設計,使用者不必瞭解具體的實現細節
- 繼承機制允許建立分等級層次的類。繼承就是子類繼承父類的特徵和行為。
- 多型是指一個物件的相同方法在不同情形有不同表現形式。
多型存在的三個必要條件:
- 繼承
- 重寫(子類繼承父類後對父類方法進行重新定義)
- 父類引用指向子類物件
面向物件的五大基本原則是什麼?
單一職責原則(SRP):一個類應該有且只有一個去改變它的理由,是指一個類的功能要單一
開放封閉原則(OCP):物件或實體應該對擴充套件開放,對修改封閉。軟體實體(類,模組,函式等等)應該是可擴充套件的,但是不可修改。因為修改程式有可能會對原來的程式造成錯誤。不能修改,但是可以新增功能,儘可能的在外邊新增新的類。
里氏替換原則(LSP):子類應當可以替換父類並出現在父類能夠出現的任何地方。
依賴倒置原則(DIP):高層次的模組不應該依賴於低層次的模組,他們都應該依賴於抽象。抽象不應該依賴於具體實現,具體實現應該依賴於抽象。簡單來說,解決耦合。一般情況下抽象的變化概率很小,讓使用者程式依賴於抽象,實現的細節也依賴於抽象。即使實現細節不斷變動,只要抽象不變,客戶程式就不需要變化。這大大降低了客戶程式與實現細節的耦合度。
介面隔離原則(ISP):介面端不應該依賴它不需要的介面,介面要儘量的小,實現類不必實現不需要的方法。
什麼是JVM、JDK、JRE
JDK:Java Development Kit,是JAVA程式開發時用的開發工具包,其內部也有JRE執行環境。
JRE:Java Runtime Environment,是JAVA程式執行時需要的執行環境,就是隻為了執行JAVA程式而不是去搞開發的話,只安裝JRE就能執行已經存在的JAVA程式了。
JVM:Java Virtual Machine,JDK、JRE內部都包含JVM,JAVA虛擬機器內部包含許多應用程式的類的直譯器和類載入器等等。
為什麼Java是跨平臺的?
Java程式編譯之後的程式碼不是能被硬體系統直接執行的程式碼,而是一種中間碼——位元組碼。然後不同的硬體平臺上安裝有不同的Java虛擬機器,由JVM來把位元組碼再翻譯成所對應的硬體平臺能夠執行的程式碼。
因此對於Java程式設計者來說,不需要考慮硬體平臺是什麼。所以Java可以跨平臺。
Java的平臺無關性是建立在Java虛擬機器的平臺有關性基礎之上的,是因為Java虛擬機器遮蔽了底層作業系統和硬體的差異。
值傳遞、引用傳遞的區別
java中方法引數傳遞方式是按值傳遞。
- 如果引數是基本型別,傳遞的是基本型別的字面量值的拷貝。
- 如果引數是引用型別,傳遞的是該參量所引用的物件在堆中地址值的拷貝。
訪問修飾符public、private、protected,以及不寫(預設)時的區別?
抽象類與抽象介面
抽象類和普通類的區別:
在Java語言中使用abstract class來定義抽象類,抽象類中並不是只能有抽象方法,和普通類一樣,同樣可以擁有成員變數和普通的成員方法
- 抽象方法的訪問修飾符必須為public和protected;
- 抽象類不能被例項化;
- 如果一個類繼承於抽象類,則子類必須實現父類的抽象方法,如果子類沒有實現父類的抽象方法,則子類必須也一個抽象類。
抽象類和介面區別:
- 抽象類內部可以有方法的實現細節,而介面中只能存在public abstract方法;
- 抽象類中的成員變數可以是各種型別的,而介面中的成員變數只能是public static final型別的;
- 介面中不能含有靜態程式碼塊以及靜態方法,而抽象類中可以有靜態程式碼曠和靜態方法;
- 一個類只能繼承一個抽象類,而一個類可以實現多個介面。
重寫與過載的區別
- 重寫是子類對父類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!
- 過載(overloading) 是在一個類裡面,方法名字相同,而引數不同。返回型別可以相同也可以不同。
基本資料型別
Java的基本資料型別有哪些
- 整型:byte, short, int, long
- 字元型:char
- 浮點型:float, double
- 布林型:boolean
- Java中最小的計算單元為位元組,1位元組=8位(bit)
各個基本資料型別的取值範圍
關鍵字
- transient 宣告不用序列化的成員域
- native 用來宣告一個方法是由與計算機相關的語言(如C/C++語言)實現的
- throw 丟擲一個異常
- throws 宣告在當前定義的成員方法中所有需要丟擲的異常
String
為什麼字串是不可變的?
String是引用型別,String變數儲存一個地址,地址指向記憶體堆中的String物件。當我們說變數不可變,有兩種不可變性:
- 變數儲存的地址不可變
- 地址指向的物件內容不可變
String變數指向的地址是可變的,他的不可變性是指第2種——地址指向的物件內容不可變。
縱覽String的方法,String類確實沒有提供能從String外部修改物件的方法。我們熟悉的replace,substring等等方法都要返回一個String,其實都是在返回一個新的物件,而沒有修改原有的物件。
為什麼要設計成物件內容不可變?
String常量池:便於實現String常量池。String存在於常量池中,當新建立一個字串變數,如果字串在記憶體中已經存在,那麼就會把這個已經存在於常量池物件的地址賦給變數。這樣可節省記憶體開銷。但是通過建構函式new String()的並不是。
執行緒安全:物件內容不可變,就不會有執行緒安全問題。
程式碼安全:如果String可變,一旦程式碼某處改動了字串,會對系統有安全和穩定性威脅。一個字串物件被多個變數引用,直接修改物件內容,引起所有引用該物件的變數都發生變化,容易引起bug。
String.valueOf和Integer.toString的區別
java.lang.Object類裡已有public方法.toString(),所以對任何嚴格意義上的java物件都可以呼叫此方法。但在使用時要注意,必須保證object不是null值,否則將丟擲NullPointerException異常。
valueOf(Object obj)對null值進行了處理,不會報任何異常。但當object為null 時,String.valueOf(object)的值是字串”null”,而不是null。
swtich對string的支援
Java1.7之前switch只能侷限於int 、short 、byte 、char四類做判斷條件。在JVM內部實際大部分位元組碼指令只有int型別的版本。在使用switch的時候,如果是非int型,會先轉為int型,再進行條件判斷。
Java1.7的switch增加了對String的支援,可String並不能直接轉為int型,switch比較的是字串常量的雜湊值(int型別),但是hash值可能會有衝突,所以還需要再呼叫equals方法進行二次比較。
intern
對於任意兩個字串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true。
在呼叫”ab”.intern()方法的時候會返回”ab”,但是這個方法會首先檢查字串池中是否有”ab”這個字串,如果存在則返回這個字串的引用,否則就將這個字串新增到字串池中,然會返回這個字串的引用。
可以利用String.intern方法來提高程式效率或者減少記憶體佔用的情況
== 和 equals 的區別是什麼
它的作用是判斷兩個物件的地址是不是相等。即,判斷兩個物件是不是同一個物件。(基本資料型別 == 比較的是值,引用資料型別 == 比較的是記憶體地址)
equals() : 它的作用也是判斷兩個物件是否相等。但它一般有兩種使用情況:
情況1:類沒有覆蓋 equals() 方法。則通過 equals() 比較該類的兩個物件時,等價於通過“==”比較這兩個物件。
情況2:類覆蓋了 equals() 方法。一般,我們都覆蓋 equals() 方法來兩個物件的內容相等;若它們的內容相等,則返回 true (即,認為這兩個物件相等)。
自動拆裝箱
什麼是包裝型別?什麼是基本型別
為什麼存在這兩種型別呢?
主要是效率,基本資料型別基於值,物件型別基於物件的引用。物件類似儲存在堆中,通過棧中的引用來使用這些物件,但是對於一些區域性變數,這個變數直接儲存“值”,並置於棧中,更加高效些。
有了基本型別為什麼還要有包裝型別呢?
我們知道Java是一個面相物件的程式語言,基本型別並不具有物件的性質,為了讓基本型別也具有物件的特徵,就出現了包裝型別(如我們在使用集合型別Collection時就一定要使用包裝型別而非基本型別),它相當於將基本型別“包裝起來”,使得它具有了物件的性質,並且為其添加了屬性和方法,豐富了基本型別的操作。
另外,當需要往ArrayList,HashMap中放東西時,像int,double這種基本型別是放不進去的,因為容器都是裝object的,這是就需要這些基本型別的包裝器類了。
二者的區別:
宣告方式不同:
基本型別不使用new關鍵字,而包裝型別需要使用new關鍵字來在堆中分配儲存空間;
儲存方式及位置不同:
基本型別是直接將變數值儲存在棧中,而包裝型別是將物件放在堆中,然後通過引用來使用;
初始值不同:
基本型別的初始值如int為0,boolean為false,而包裝型別的初始值為null;
使用方式不同:
基本型別直接賦值直接使用就好,而包裝型別在集合如Collection、Map時會使用到。
什麼是自動拆裝箱
有了基本資料型別和包裝類,肯定有些時候要在他們之間進行轉換。比如把一個基本資料型別的int轉換成一個包裝型別的Integer物件。
我們認為包裝類是對基本型別的包裝,所以,把基本資料型別轉換成包裝類的過程就是打包裝,英文對應於boxing,中文翻譯為裝箱。反之,把包裝類轉換成基本資料型別的過程就是拆包裝,英文對應於unboxing,中文翻譯為拆箱。
在Java SE5中,為了減少開發人員的工作,Java提供了自動拆箱與自動裝箱功能。
Integer i =10; //自動裝箱
int b= i; //自動拆箱
Integer的快取機制是什麼
Integer裡面預設的快取數字是-128-127,
1、Integer與Integer相互比較,資料在-128-127範圍內,就會從快取中拿去資料,使用== 比較就相等;如果不在這個範圍,就會直接新建立一個Integer物件,使用 == 判斷的是兩個記憶體的應用地址,所以不相等。
2、Integer和int型別相比,在jdk1.5會自動拆箱,然後==比較棧記憶體中的資料,所以都是相等的
異常
異常型別
自定義異常
- 是否需要定義成 Checked Exception(檢查性異常必須在編寫程式碼時,使用 try catch 捕獲),因為這種型別設計的初衷更是為了從異常情況恢復,作為異常設計者,我們往往有充足資訊進行分類。
- 在保證診斷資訊足夠的同時,也要考慮避免包含敏感資訊,因為那樣可能導致潛在的安全問題。如果我們看 Java 的標準類庫,你可能注意到類似 java.net.ConnectException,出錯資訊是類似“ Connection refused (Connection refused)”,而不包含具體的機器名、IP、埠等,一個重要考量就是資訊保安。類似的情況在日誌中也有,比如,使用者資料一般是不可以輸出到日誌裡面的。
Error 和 Exception
首先 Exception 和 Error 都是繼承於 Throwable 類,在 Java 中只有 Throwable 型別的例項才可以被丟擲(throw)或者捕獲(catch),Exception 和 Error 體現了JAVA 這門語言對於異常處理的兩種方式。
Exception 是 Java 程式執行中可預料的異常情況,我們可以獲取到這種異常,並且對這種異常進行業務外的處理。
分為檢查性異常和非檢查性(RuntimeException)異常。兩個根本的區別在於,檢查性異常必須在編寫程式碼時,使用 try catch 捕獲,比如:IOException異常。非檢查性異常在程式碼編寫時,可以忽略捕獲操作,比如:ArrayIndexOutOfBoundsException,這種異常是在程式碼編寫或者使用過程中通過規範可以避免發生的。
Error 是 Java 程式執行中不可預料的異常情況,這種異常發生以後,會直接導致 JVM 不可處理或者不可恢復的情況。所以這種異常不可能抓取到,比如 OutOfMemoryError、NoClassDefFoundError等。
finally和return的執行順序
- 不管有沒有出現異常,finally塊中程式碼都會執行;
- 當try和catch中有return時,finally仍然會執行;
- finally是在return後面的表示式運算後執行的(此時並沒有返回運算後的值,而是先把要返回的值儲存起來,管finally中的程式碼怎麼樣,返回的值都不會改變,仍然是之前儲存的值),所以函式返回值是在finally執行前確定的;
- finally中最好不要包含return,否則程式會提前退出,返回值不是try或catch中儲存的返回值。
泛型
泛型與繼承
泛型:引數化型別,將型別由原來的具體型別引數化,把型別也定義為行參。方法中均使用同一型別。屬於編譯期資訊,無法提供動態繫結,當型別與方法無關時,使用泛型。使用泛型的類應該有共同的方法,為水平方法,而繼承是垂直方向。
型別擦除是什麼
List和List等型別,在編譯後都會變成List
泛型中 K T V R ? object等的含義
- K:key(鍵值);
- T:Type(Java類);
- V:Value(值);
- N:Number(數值型別);
- E:Element(元素,集合元素);
- ?:無限定
- Object:所有類的根類
限定萬用字元和非限定萬用字元
- 表示型別的上界:<? Extends T>,型別必須為T或者其子類
- 表示型別的下界:<? Super T>,型別必須為T或者T的父類
List<?>和List之間的區別
- List:可以新增任意型別的元素,不安全(編譯通過),不便利(需要自己強制轉換型別),不表述(可以直接看到實參就是其型別)。
- List<?>:萬用字元型別,接受List任意引數化型別,包括List,不能新增元素,保證安全和便利,但不保證表述。
- List< Object> 引數為物件,可以新增List,可以新增元素,但不能接受除了本身外的任何引數化型別。
序列化
什麼是序列化和反序列化
- 序列化:物件儲存轉換為二進位制,物件和元資料(屬性)都儲存為二進位制。
- 反序列化:把物件和元資料從二進位制恢復。
- 場景:持久化,存入資料庫;遠端傳輸,程序之間傳輸。
為什麼要序列化
有些時候我們需要把應用程式中的資料以另一種形式進行表達,以便於將資料儲存起來,並在未來某個時間點再次使用,或者便於通過網路傳輸給接收方。這一過程我們把它叫做序列化。
序列化的底層原理
實現的API:ObjectOutputStream中的writeObject(Object obj),ObjectInputStream中的readObject()
只有實現了Serializable或者Externalizable介面的類的物件才可以被例項化。父類實現上述介面,子類就不需要顯示的實現,靜態類不可以實現序列化。
序列化與單例模式
正常的序列化會破壞單例模式,產生新的物件,保證單例時,需要新增private Object readResolve(){}方法
為什麼說序列化並不安全
可以實現遠端程式碼執行;序列化明文儲存,可以根據物件序列化生產很多私有屬性。通過反序列化產生非預期物件,根源在於ObjectInputSteam對生成的物件型別沒有限制。
單元測試
junit
單元測試各個註解執行的先後順序:
@BeforeClass -> @Before -> @Test -> @After -> @AfterClass
時間處理
Java時間API
- Util.Date:能夠精確到毫秒級
- Sql.Date:資料庫日期API,操作日期,不能讀取和修改時間
- Sql.Time:資料庫時間類,獲取操作時間
- Sql.Timestamp:納秒級Util.Date
- System.currentTimeMillis():當前系統時間,毫秒
- System.nanoTime(); 當前系統時間,納秒
SimpleDateFormat執行緒安全問題
主要因為SimpleDateFormat繼承於DateFormat,而DateFormat使用成員變數傳值,其Calendar在多個方法中呼叫,Format和subFormat都使用了DateFormat的成員變數Calendar
Java 8 對時間的處理
引入新類java.time:執行緒安全,不可變。主要類:
- Instant:時間戳
- LocalDate:不包含具體時間的日期,2014-01-01
- LocalTime:不包含日期的時間
- LocalDateTime:包含時間於日期,但無時區偏移
- ZonedDateTime:完整時間,偏移量以GMT和UTC為準。
註解
什麼是元註解
註解的註解:@Retention、@Target、@Document、@Inherited
- @Retentiond:定義註解的保留策略,RetentionPolicy.SOURCE:在原始碼 .CLASS在位元組碼,執行時無效,.RUNTIME在位元組碼。反射可以獲取
- @Target:定義註解的目標
- @Document:說明註解被包含在Javadoc中
- @Inherited:說明子類可以繼承父類的該註解
怎麼自定義註解
Public @interface xx{} java.lang.annotation
Java中有哪些常用註解
- @Override:重寫標誌,標示覆蓋父類的方法
- @Deprecated: 已過期,表示方法時不建議使用的。
- @SuppressWarnings: 壓制告警,抑制告警
什麼是SPI?
SPI ,全稱為 Service Provider Interface,是一種服務發現機制。它通過在ClassPath路徑下的META-INF/services資料夾查詢檔案,自動載入檔案裡所定義的類。
這一機制為很多框架擴充套件提供了可能,比如在Dubbo、JDBC中都使用到了SPI機制。
反射有什麼用
反射(Reflection)是其Java非常突出的一個動態相關機制。它可以於執行時載入、使用編譯期間完全未知的classes(但是要知道類的名字)。也就是說,在執行時,我們還可以獲取一個類中所有的方法和屬性,可以例項化任何類的物件,還能判斷一個物件所屬的類。可以用於熱部署。
反射最重要的用途就是開發各種通用框架。
很多框架(比如 Spring)都是配置化的(比如通過 XML 檔案配置 Bean),為了保證框架的通用性,它們可能需要根據配置檔案載入不同的物件或類,呼叫不同的方法,這個時候就必須用到反射,執行時動態載入需要載入的物件。
Class類
Java中,無論生成某個類的多少個物件,這些物件都會對應於同一個Class物件,這個Class物件是由JVM生成的,通過它能夠獲悉整個類的結構。要想使用反射,首先需要獲得待操作的類所對應的Class物件。
獲取class物件方法:
1.使用Class類的靜態方法
- eg:Class.forName(“java.lang.String”);
2.使用類的.class語法
- eg:Class c = Employee.class;
3.使用物件的getClass()方法
- eg:Employee e = new Employee();
- Class c3 = e.getClass();
原理
所有的java類都是繼承了object這個類,在object這個類中有一個方法:getclass().這個方法是用來取得該類已經被例項化了的物件的該類的引用,這個引用指向的是Class類的物件。我們自己無法生成一個Class物件(建構函式為private),而 這個Class類的物件是在當各類被調入時,由 Java 虛擬機器自動建立 Class 物件,或通過類裝載器中的 defineClass 方法生成。我們生成的物件都會有個欄位記錄該物件所屬類在CLass類的物件的所在位置。如下圖所示:
IO
字元流、位元組流、輸入流、輸出流的區別
位元組流:
- 位元組流在操作的時候不會用到緩衝區(也就是記憶體)
- 位元組流可用於任何型別的物件,包括二進位制物件
- 位元組流處理單元為1個位元組,操作位元組和位元組陣列。
InputStream是所有位元組輸入流的祖先,而OutputStream是所有位元組輸出流的祖先。
字元流:
- 而字元流在操作的時候會用到緩衝區
- 而字元流只能處理字元或者字串
- 字元流處理的單元為2個位元組的Unicode字元,操作字元、字元陣列或字串,Reader是所有讀取字串輸入流的祖先,而writer是所有輸出字串的祖先。
同步與非同步的區別
同步和非同步關注的是訊息通訊機制 (synchronous communication/ asynchronous communication)
所謂同步,就是在發出一個呼叫時,在沒有得到結果之前,該呼叫就不返回。但是一旦呼叫返回,就得到返回值了。 換句話說,就是由呼叫者主動等待這個呼叫的結果。
而非同步則是相反,呼叫在發出之後,這個呼叫就直接返回了,所以沒有返回結果。換句話說,當一個非同步過程呼叫發出後,呼叫者不會立刻得到結果。而是在呼叫發出後,被呼叫者通過狀態、通知來通知呼叫者,或通過回撥函式處理這個呼叫。典型的非同步程式設計模型比如Node.js
舉個通俗的例子:
你打電話問書店老闆有沒有《分散式系統》這本書,如果是同步通訊機制,書店老闆會說,你稍等,”我查一下”,然後開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結果(返回結果)。
而非同步通訊機制,書店老闆直接告訴你我查一下啊,查好了打電話給你,然後直接掛電話了(不返回結果)。然後查好了,他會主動打電話給你。在這裡老闆通過“回電”這種方式來回調。
實質:訪問資料的方式,同步需要當前執行緒讀寫資料,在讀寫資料的過程中還是會阻塞;非同步只需要I/O操作完成的通知,當前程序並不主動讀寫資料,由作業系統核心完成資料的讀寫。
阻塞與非阻塞的區別
阻塞和非阻塞關注的是程式在等待呼叫結果(訊息,返回值)時的狀態.
阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。呼叫執行緒只有在得到結果之後才會返回。
非阻塞呼叫指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒。
還是上面的例子,
你打電話問書店老闆有沒有《分散式系統》這本書,你如果是阻塞式呼叫,你會一直把自己“掛起”,直到得到這本書有沒有的結果,如果是非阻塞式呼叫,你不管老闆有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老闆有沒有返回結果。
在這裡阻塞與非阻塞與是否同步非同步無關。跟老闆通過什麼方式回答你結果無關。
Linux5種IO模型
阻塞IO模型、非阻塞IO模型、IO複用模型、訊號驅動IO模型以及非同步IO模型。
BIO、NIO和AIO的區別
同步阻塞IO(JAVA BIO):
同步並阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。
同步非阻塞IO(Java NIO):
同步非阻塞,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。使用者程序也需要時不時的詢問IO操作是否就緒,這就要求使用者程序不停的去詢問。
非同步阻塞IO(Java NIO):
此種方式下是指應用發起一個IO操作以後,不等待核心IO操作的完成,等核心完成IO操作以後會通知應用程式,這其實就是同步和非同步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼為什麼說是阻塞的呢?因為此時是通過select系統呼叫來完成的,而select函式本身的實現方式是阻塞的,而採用select函式有個好處就是它可以同時監聽多個檔案控制代碼(如果從UNP的角度看,select屬於同步操作。因為select之後,程序還需要讀寫資料),從而提高系統的併發性!
非同步非阻塞IO(Java AIO(NIO.2)):
在此種模式下,使用者程序只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程式會得到IO操作完成的通知,此時使用者程序只需要對資料進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由核心完成了。
三種IO的用法和原理
- BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解。
- NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援。
- AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。
正則表示式
- 用\d可以匹配一個數字,\w可以匹配一個字母或數字
- 在正則表示式中,用*表示任意個字元(包括0個),用+表示至少一個字元,用?表示0個或1個字元,用{n}表示n個字元,用{n,m}表示n-m個字元
要做更精確地匹配,可以用[]表示範圍,比如:
- [0-9a-zA-Z_]可以匹配一個數字、字母或者下劃線;
- [0-9a-zA-Z_]+可以匹配至少由一個數字、字母或者下劃線組成的字串,比如’a100’,‘0_Z’,'Py3000’等等;
- [a-zA-Z_][0-9a-zA-Z_]*可以匹配由字母或下劃線開頭,後接任意個由一個數字、字母或者下劃線組成的字串,也就是Python合法的變數;
- [a-zA-Z_][0-9a-zA-Z_]{0, 19}更精確地限制了變數的長度是1-20個字元(前面1個字元+後面最多19個字元)。
想要學習更多的知識可以,工眾號:程式設計領域
Java初學者學習教程: