Java
1、泛型
泛型類
對象執行個體化是不指定泛型,預設為:Object;
泛型不同的引用不能互相指派;
A<String> a1 = new A<String>();
a1.setKey("Estella");
System.out.println(a1.getKey());
A<Integer> a2 = new A<Integer>();
a2.setKey(666);
System.out.println(a2.getKey());
class A<T>{
private T key;
public void setKey(T key){
this.key = key;
}
public T getKey() {
return this.key;
}
}
泛型方法
在泛型方法中可以定義泛型參數,此時,參數的類型就會傳入資料的類型;
在靜态方法中,不能使用類定義泛型,隻能使用靜态方法自己定義的泛型;
在調用泛型方法時,傳入的參數是什麼類型,就會把泛型改成什麼類型;
public class DAO{
//通路修飾符後面加<泛型>
public <E> void show(E e){
System.out.println(e.toString);
}
public <T> T show1(T t){
return t;
}
}
泛型接口
未傳入泛型實參時,與泛型類的定義相同,在聲明類的時候,需要将泛型的聲明也一起加到類中;
如果實作接口時指定接口泛型的具體資料類型,這個類實作接口的所有方法的位置都要将泛型替換為實際的具體資料類型;
interface Generator<T>{
T next();
}
class Gen<T> implements Generator<T>{
@Override
public T next(T t) {
return t;
}
}
class gener implements Generator<String>{
public String next(String t){
return t;
}
}
泛型通配符
List<?> list;
有限制的通配符
2、枚舉
枚舉類中的每個枚舉都是單例模式的;
枚舉類也可以實作一個或多個接口;
若需要每個枚舉值在調用實作的接口方法呈現出不同的行為方式,則可以讓每個枚舉值分别來實作該方法;
public class Test {
public static void main(String[] args) {
Season spring = Season.SPRING;
spring.showInfo();
Season winter = Season.WINTER;
winter.showInfo();
}
}
enum Season{
SPRING("春天","喜歡波波"),//相當于調用了Season的有參的私有構造方法
SUMMER("夏天","喜歡波波"),
AUTUMN("秋天","喜歡波波"),
WINTER("冬天","喜歡波波");
private final String name;
private final String desc;
private Season(String name,String desc){
this.name = name;
this.desc = desc;
}
public void showInfo(){
System.out.println(this.name+'\t'+this.desc);
}
}
3、注解Annotation
Annotation可以向修飾符一樣被使用,可用于修飾包、類、構造器、方法、成員變量、參數、局部變量的聲明,這些資訊被儲存在Annotation的“name=value”對中。
Annotation能被用來為程式元素設定中繼資料。
使用Annotation時要在其前面增加@符号,并把該Annotion當成一個修飾符使用,用于修飾它支援的程式元素。
作用在代碼的注解是
- @Override - 檢查該方法是否是重寫方法。如果發現其父類,或者是引用的接口中并沒有該方法時,會報編譯錯誤。
- @Deprecated - 标記過時方法。如果使用該方法,會報編譯警告。
- @SuppressWarnings - 訓示編譯器去忽略注解中聲明的警告。
作用在其他注解的注解(或者說 元注解)是:
- @Retention - 辨別這個注解怎麼儲存,是隻在代碼中,還是編入class檔案中,或者是在運作時可以通過反射通路。
- @Documented - 标記這些注解是否包含在使用者文檔中。
- @Target - 标記這個注解應該是哪種 Java 成員。
- @Inherited - 标記這個注解是繼承于哪個注解類(預設 注解并沒有繼承于任何子類)
從 Java 7 開始,額外添加了 3 個注解:
- @SafeVarargs - Java 7 開始支援,忽略任何使用參數為泛型變量的方法或構造函數調用産生的警告。
- @FunctionalInterface - Java 8 開始支援,辨別一個匿名函數或函數式接口。
- @Repeatable - Java 8 開始支援,辨別某注解可以在同一個聲明上使用多次。
自定義注解
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}
@interface 用來聲明 Annotation,@Documented 用來表示該 Annotation 是否會出現在 javadoc 中, @Target 用來指定 Annotation 的類型,@Retention 用來指定 Annotation 的政策。
上面的作用是定義一個 Annotation,它的名字是 MyAnnotation1。定義了 MyAnnotation1 之後,我們可以在代碼中通過 “@MyAnnotation1” 來使用它。 其它的,@Documented, @Target, @Retention, @interface 都是來修飾 MyAnnotation1 的。下面分别說說它們的含義:
(01) @interface
使用 @interface 定義注解時,意味着它實作了 java.lang.annotation.Annotation 接口,即該注解就是一個Annotation。
定義 Annotation 時,@interface 是必須的。
注意:它和我們通常的 implemented 實作接口的方法不同。Annotation 接口的實作細節都由編譯器完成。通過 @interface 定義注解後,該注解不能繼承其他的注解或接口。
(02) @Documented
類和方法的 Annotation 在預設情況下是不出現在 javadoc 中的。如果使用 @Documented 修飾該 Annotation,則表示它可以出現在 javadoc 中。
定義 Annotation 時,@Documented 可有可無;若沒有定義,則 Annotation 不會出現在 javadoc 中。
(03) @Target(ElementType.TYPE)
前面我們說過,ElementType 是 Annotation 的類型屬性。而 @Target 的作用,就是來指定 Annotation 的類型屬性。
@Target(ElementType.TYPE) 的意思就是指定該 Annotation 的類型是 ElementType.TYPE。這就意味着,MyAnnotation1 是來修飾"類、接口(包括注釋類型)或枚舉聲明"的注解。
定義 Annotation 時,@Target 可有可無。若有 @Target,則該 Annotation 隻能用于它所指定的地方;若沒有 @Target,則該 Annotation 可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME)
前面我們說過,RetentionPolicy 是 Annotation 的政策屬性,而 @Retention 的作用,就是指定 Annotation 的政策屬性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定該 Annotation 的政策是 RetentionPolicy.RUNTIME。這就意味着,編譯器會将該 Annotation 資訊保留在 .class 檔案中,并且能被虛拟機讀取。
定義 Annotation 時,@Retention 可有可無。若沒有 @Retention,則預設是 RetentionPolicy.CLASS。
4、IO流
java.io.File類的使用
IO原理及流的分類
IO流用來處理裝置之間的資料傳輸;
java程式中,對于資料的輸入/輸出操作以“流Stream”的方式進行;
java.io包下提供了各種“流”類和接口,用以擷取不同種類的資料,并通過标準的方法輸入或輸出資料;
檔案流(資料流的讀寫都是基于檔案的操作,計算機與硬碟之間發生的io操作)
FileInputStream/FileOutputStream/FileReader/FileWriter
檔案位元組流FileInputStream/FileOutputStream:
try{
FileInputStream in = new FileInputStream("C:\\Users\\HP\\Desktop\\test.txt");
//位元組流,設定一個byte數組結束歐讀取檔案的内容
byte[] b = new byte[100];
//read方法傳回值是讀取資料的長度,當檔案讀取完畢,還會向後讀取,這時傳回值為-1
//傳回值為-1說明檔案讀取完畢
int len = 0;
while((len = in.read(b))!=-1){
System.out.println(new String(b,0,len));
}
//流在使用完畢後需要關閉!
in.close();
}catch(IOException e){
e.printStackTrace();
}
try{
FileOutputStream out = new FileOutputStream("C:\\Users\\HP\\Desktop\\test.txt");
//預設覆寫原有内容,追加内容添加方法參數
//new FileOutputStream("路徑",true)
String str = "goodgoodstudy";
//把資料寫到記憶體
out.write(str.getBytes());
//把記憶體中的資料刷寫到硬碟
out.flush();
//關閉流
out.close();
}catch (IOException e){
e.printStackTrace();
}
注:檔案位元組流很通用,可以用來操作字元的文檔,還可以操作任何其他類型檔案(圖檔,壓縮包等等),因為位元組流直接使用二進制;
檔案字元流:FileReader/FileWriter
try{
FileReader fr = new FileReader("C:\\Users\\HP\\Desktop\\test.txt");
char[] c = new char[100];
int len = 0;
while((len = fr.read(c))!=-1){
System.out.println(new String(c,0,len));
}
fr.close();
}catch(IOException e){
e.printStackTrace();
}
緩沖流(資料流的讀寫都是基于記憶體的操作)
BufferedInputStream/BufferedOutputStream/BufferedReader/BufferedWriter
緩沖流就是先把資料緩沖到記憶體裡,在記憶體中去做io操作,基于記憶體的io操作大概比基于硬碟的io操作快75000倍;
//檔案位元組輸入流對象
FileInputStream fi = new FileInputStream("F:\\src\\jkl\\jkl.txt");
//把檔案位元組輸入流放到緩沖位元組輸入流對象
BufferedInputStream br = new BufferedInputStream(fi);
byte[] b = new byte[10];
int len = 0;
while ((len = br.read(b))!=-1){
System.out.println(new String(b,0,len));
}
br.close();
fi.close();
轉換流
InputStreamReader/OutputStreamWriter
//位元組流轉換為字元流,參數1是位元組流,參數2是編碼
InputStreamResder in = new InputStreamReader(new FileInputStream(""),"GBK");
//...
in.close();
标準輸入輸出流
System.in和System.out分别代表了系統标準的輸入和輸出裝置,預設輸入裝置是鍵盤,預設輸出裝置是顯示器;
System.in的類型是InputStream;
System.out的類型是PrintStream;
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
BufferedWriter out = new BufferedWriter(new FileWriter("F:\\javaproject\\jproject\\src\\jkl\\jkl.txt",true));
String line = "";
while ((line = br.readLine())!=null){
if(line.equals("over"))
break;
out.write(line);
}
out.flush();
out.close();
br.close();
in.close();
列印流
資料流
對象流
把對象儲存到電腦的硬碟上,需要把對象轉化為一個二進制的位元組流,把這個流儲存到電腦上,要使用對象,需要把流轉化為對象再使用。
對象的網絡傳輸也需要對象與流的轉換。
正是因為這兩個原因,就産生了對象的輸入流和輸出流。
用ObjectOutputStream類将一個Java對象寫入IO流中稱為序列化,Serialize。
用ObjectInputStream類從IO流中恢複該java對象稱為反序列化,Deserialize。
ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量。
序列化和反序列化針對的是對象的各種屬性,不包括累的屬性。
public class Person implements Serializable {
/**
* 一個表示序列化版本辨別符的靜态變量
* 用來表明類的不同版本間的相容性
*/
private static final long serialVersionUID = 1L;
String name;
int age;
}
public class Test {
public static void main(String[] args) throws Exception {
testSerialize();
testDeserialize();
}
/**
* 對象的序列化
*/
public static void testSerialize()throws Exception {
//定義對象的輸出流,把對象的序列化之後的流放到指定的穩健者
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("F:\\javaproject\\jproject\\src\\jkl\\jkl.txt"));
Person p = new Person();
p.name="zhangsan";
p.age = 29;
out.writeObject(p);
out.flush();//刷寫資料到硬碟
out.close();
}
/**
* 對象的反序列化
*/
public static void testDeserialize()throws Exception{
//建立對象輸入流對象,從指定檔案中把對象序列化後的流讀取出來
ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:\\javaproject\\jproject\\src\\jkl\\jkl.txt"));
Object obj = in.readObject();
//對象類型強轉
Person p = (Person) obj;
System.out.println(p.name);
System.out.println(p.age);
in.close();
}
}
對象的序列化與反序列化使用的類要嚴格一緻,包名、類名、類機構等等所有都要一緻。
RandomAccessFile類
支援“随機通路”的方式,程式可以直接跳到檔案的任意地方來讀寫檔案;
支援隻通路檔案的部分内容;
可以向已存在的内容後追加内容;
RandomAccessFile對象包含一個記錄指針,用以标示目前讀寫的位置;
RandomAccessFile類對象可以自由移動記錄指針;
long getFilePointer():擷取檔案記錄指針的目前位置;
void seek(long pos):将檔案記錄指針定位到pos位置;
public static void testRandomAccessFileRead()throws Exception{
//參數1是通路路徑,參數2是mode,通路模式
RandomAccessFile ra = new RandomAccessFile("F:\\jkl.txt","r");
//設定讀取檔案内容的起始點
ra.seek(4);
byte[] b = new byte[1024];
int len = 0;
while ((len=ra.read(b))!=-1){
System.out.println(new String(b,0,len));
}
ra.close();
}
public static void testRandomAccessFileWrite()throws Exception{
//參數1是通路路徑,參數2是mode,通路模式
RandomAccessFile ra = new RandomAccessFile("F:\\javaproject\\jproject\\src\\jkl\\jkl.txt","rw");
//設定寫入檔案内容的起始點,0從開頭寫,ra.length是檔案的追加
//如果是在開頭或者中間位置追加,會覆寫等長的原有内容
ra.seek(ra.length());
ra.write("hello!!".getBytes(StandardCharsets.UTF_8));
ra.close();
}
如果是在開頭或者中間位置追加,會覆寫等長的原有内容。
小結
流是用來處理資料的;
處理資料時,一定要先明确資料源,與資料目的地;
資料源可以是檔案、鍵盤;資料目的地可以是檔案、顯示器或者其他裝置;
流隻是在幫助資料進行傳輸并對傳輸的資料進行處理,比如過濾處理、轉換處理等。
位元組流-緩沖流
輸入流 InputStream-FileInputStream-BufferedInputStream
輸出流 OutputStream-FileOutputStream-BufferedOutputStream
字元流-緩沖流
輸入流 Reader-FileReader-BufferedReader
輸出流 Writer-FileWriter-BufferedWriter
**對象流 ObjectInputStream-ObjectOutputStream
每日程式設計
456. 132 模式
難度中等451收藏分享切換為英文接收動态回報
給你一個整數數組
nums
,數組中共有
n
個整數。132 模式的子序列 由三個整數
nums[i]
、
nums[j]
和
nums[k]
組成,并同時滿足:
i < j < k
和
nums[i] < nums[k] < nums[j]
。
如果
nums
中存在 132 模式的子序列 ,傳回
true
;否則,傳回
false
。
**進階:**很容易想到時間複雜度為
O(n^2)
的解決方案,你可以設計一個時間複雜度為
O(n logn)
或
O(n)
的解決方案嗎?
示例 1:
輸入:nums = [1,2,3,4]
輸出:false
解釋:序列中不存在 132 模式的子序列。
示例 2:
輸入:nums = [3,1,4,2]
輸出:true
解釋:序列中有 1 個 132 模式的子序列: [1, 4, 2] 。
示例 3:
輸入:nums = [-1,3,2,0]
輸出:true
解釋:序列中有 3 個 132 模式的的子序列:[-1, 3, 2]、[-1, 3, 0] 和 [-1, 2, 0] 。
提示:
-
n == nums.length
-
1 <= n <= 104
-
-109 <= nums[i] <= 109
思路
暴力求解,嵌套雙循環,時間複雜度 O(N2) ;
class Solution(object):
def find132pattern(self, nums):
n = len(nums)
numsi = nums[0]
for j in range(1, n):
for k in range(n - 1, j, -1):
if numsi < nums[k] and nums[k] < nums[j]:
return True
#維護右邊最小值
numsi = min(numsi, nums[j])
return False
單調棧:所謂單調棧就是棧中的元素都是依次遞增或者遞減的,進而友善我們能維護好數組的一個區間内的「最大值」「次大值」等等。