java-day34
文章目錄
泛型的回顧
泛型參數聲明的位置:
在類上面聲明:泛型類
在接口上面聲明:泛型接口
在方法上面聲明:泛型方法
使用<>就可以聲明泛型參數:
public class Point<T>{}
public interface Action<T>{}
public <T> void test(T t);
確定泛型形參的具體類型:
public class Point<T>{}
main:
Point<Integer> p;
public interface Action<T>{}
main:
Action<String> a;
public <T> void test(T t);
main:
t.test(1);
t.test("hello");
public <T> T test();
main:
int a = t.test();
String s = t.test();
public <T> T test(T t);
main:
int a = t.test(1);
String s = t.test("hello");
泛型的作用:
泛型是對代碼中的類型進行參數化。本來在代碼中寫死的類型,可以通過泛型變的更加靈活,將來在使用代碼的時候,通過傳參數的方式,再來確定代碼中到底使用的是什麼類型。
public class Point{
private int x;
private int y;
}
main:
Point p = new Point();
public class Point<T>{
private T x;
private T y;
}
main:
Point<Integer> p = new Point<>();
Point<String> p = new Point<>();
泛型雖然可以讓之前代碼中寫死的類型變化起來,但是泛型也增加了類型的複雜度:
public class Point{
private int x;
private int y;
}
這個類無論如何使用,Point只有一種類型,就是Point
public class Point<T>{
private T x;
private T y;
}
無法使用多態
這個帶泛型的類型,通過傳不同的泛型參數的類型,在編譯期間會產生很多不同的Point類型
例如:Point Point Point 等等,並且這些類型之間都是不兼容的,這就意味着無法使用多態。
//編譯報錯
Point<Object> p = new Point<String>();
?號通配符
爲了使用多態,那麼必須找出一個可以和其他泛型類型都兼容的類型,那麼這時候可以使用?號通配符
//編譯通過
Point<?> p = new Point<任意類型>();
但是使用通配符之後,也會產生一些限制,由於使用了?號通配符,那麼編譯器就無法確定這個泛型的類型到底是什麼,如果這時候我們數去操作這個參數數據的時候,編譯器就會報錯了,因爲編譯器無法確定這個參數數據的類型和?號通配符將來代表的類型是否一致。但是如果調用的方法沒有參數,或者參數的類型並不是用泛型類型所表示的,那麼編譯器就不管了。
public class Point<T>{
private T x;
public void setX(T x){
this.x = x;
}
public String toString(){
return x;
}
public void say(String name){
//...
}
}
Point<?> p = new Point<String>();
p.setX(1);
通配符?號結合extends和super
通配符?號表示的類型範圍太大,可以結合extends和super來進一步限制通配符所表示的類型方法:
//表示很大的一個類型方法
List<?> list = new ArrayList<任意類型>();
//限制通配符可以表示的類型範圍(上限)
List<? extends Number> list = new ArrayList<Number類型>();
List<? extends Number> list = new ArrayList<Number的子類型>();
//限制通配符可以表示的類型範圍(下限)
List<? super Number> list = new ArrayList<Number類型>();
List<? super Number> list = new ArrayList<Number的父類型>();
泛型擦除:
泛型只能在編譯的時候起作用,編譯後代碼中的泛型信息就被擦除掉了。List List List 等等這些類型在編譯的時候是不兼容的類型,但是編譯之後,它們都是同一種類型,因爲編譯之後泛型的類型在代碼中會被擦除掉。
代碼中和泛型相關的有倆個地方:
1.類上面、接口上面、方法上面聲明的泛型信息
2.在代碼中,使用泛型類、泛型接口、泛型方法時,所傳的實際的泛型參數信息。
編譯後泛型信息的擦除,指的是第二種情況,也就是實際在使用泛型的時候所傳的泛型類型參數會被擦除。
編譯前的源碼
package com.zzb.day34;
public class Point<T> {
private T x;
public void setX(T x){
this.x = x;
}
public T getX(){
return x;
}
public String toString(){
return "x="+x;
}
}
class PointTest{
public static void main(String[] args){
Point<Integer> p = new Point<>();
p.setX(1);
System.out.println(p);
}
}
反編譯後的源碼
枚舉enum
1、簡單的使用一下
public enum Gender{
MALE,FEMALE;
}
Gender就是一個枚舉類型,美劇類型也是一種類(class),是一種特殊的類,枚舉類型中所列出的元素,就是枚舉類型的對象,默認用public static final修飾的,所以這些對象的名字全字母大寫。
2、枚舉的意義
java中的類很多都是可以創建無數對象(從語法形式上來講),但是從實際意義上只需要個別對象就可以了,如果創建多個對象就會佔用多餘內存空間也沒有什麼實際意義。枚舉類型的對象是有限的個數甚至是固定的對象個數。
如果java中,有一種類,它的對象個數和名字都是固定不變的,那麼就可以使用枚舉類表示。
例如Gender類型,表示性別:一個MALE,另一個FEMALE。
3、枚舉類型中,可以提前固定該類型的個數和名字
public enum Gender{
MALE,FEMALE;
}
除此之外,我們不能再創建這個類型的其他新的對象。(構造器是私有的)
public class GenderTest {
public static void main(String[] args){
Gender g;
g = Gender.MALE;
System.out.println(g);
g = Gender.FEMALE;
System.out.println(g);
}
}
枚舉類型和類之間的關係
使用javap命令把Gender.class文件進行反向解析:
得到結論:
1、枚舉類型是一個類,同時還是一個final修飾的類
2、枚舉類型都會默認繼承java.lang.Enum,並且還是一個泛型類
3、枚舉中所定義的對象其實就是類裏面的公共的靜態常量(public static final),並且常量在靜態代碼塊中做初始化。
4、枚舉類型中還有一個默認的私有構造器
5、枚舉類型中還有給默認添加進來的方法:
values()方法:返回枚舉對象的所有對象,返回類型是數組
valueOf(String str)方法:通過一個字符串可以返回枚舉對象,這個字符串參數就是枚舉對象的名字。
6、從父類繼承過來的方法:
String name() : 返回這個枚舉對象的名字
int ordinal() :返回這個枚舉對象的編號,默認從0開始
獲得枚舉類型的指定名字的對象(3種)
方式一:
//只能獲取到Gender中的指定對象MALE
//因爲沒有可以變化的地方(字符串)
Gender g = Gender.MALE;
方式二:
//能獲取到Gender中的任意-一個對象
//因爲name是字符串類型的變量,可以任意變化
String name = "MALE";
Gender g = Gender.valueOf("MALE");
方式三:
//通過字符串確定是哪一個枚舉類型
Class c = class.forName("com.zzb.day34.Gender");
//通過字符串確定是哪一個名字的枚舉對象
String name = "FEMALE";
//可以通過改變字符串,獲取到Java中任意一個指定名字的枚舉對象
Enum g = Enum.valueOf(Gender.class,"FEMALE");
枚舉類型中定義方法
public enum Gender {
MALE,FEMALE;
public void test(){
System.out.println("Gender test...");
}
public static void print(String name){
System.out.println("hello "+name);
}
}
main:
Gender g = Gender.MALE;
g.test();
Gender.print("zzb");
枚舉類型中定義自己的屬性
public enum Gender {
MALE,FEMALE;
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
main:
Gender g = Gender.MALE;
g.setName("男生");
System.out.println(g.getName());
枚舉類型中定義自己的構造器
public enum Gender {
MALE,FEMALE;
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
private Gender(){}
private Gender(String name){
this.name = name;
}
}
枚舉中的構造器只能用private修飾,不寫也是默認爲private,同時可以定義多個不同參數列表的構造器。這些構造器在枚舉類型的外部是不能使用的,只能在枚舉類型中列出對象的時候,去指定哪個構造器來創建這個對象。
枚舉類型中構造器的調用
1、調用無參構造器
public enum Gender {
MALE(),FEMALE();
private Gender(){
System.out.println("無參構造器被調用");
}
}
main:
Gender g = Gender.MALE;
注意,這裏的枚舉類型中倆個對象MALE、FEMALE都默認是調用了無參構造器創建而成,我們可以指明調用了無參構造器
2、調用有參構造器
public enum Gender {
MALE("男生"),FEMALE("女生");
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
private Gender(){
System.out.println("無參構造器被調用");
}
private Gender(String name){
this.name = name;
System.out.println("有參構造器被調用,參數name:"+name);
}
}
main:
Gender g = Gender.MALE;
注意,枚舉類型的對象,是在這個枚舉類型進行類加載的時候,完成的對象初始化工作,因爲這些對象都是public static final修飾的。枚舉中有一個靜態代碼塊,在這裏面做初始化工作,在裏面創建構造器。
枚舉類類型中定義抽象方法
public enum Gender {
MALE(){
public void test(){
System.out.println("男生測試");
}
},
FEMALE(){
public void test(){
System.out.println("女生測試");
}
};
public abstract void test();
}
main:
Gender g = null;
g = Gender.MALE;
g.test();
g = Gender.FEMALE;
g.test();
注意,在枚舉類型中定義的抽象方法,需要在聲明枚舉類型對象的時候進行實現。
枚舉類型實現接口
枚舉類型有默認的父類型java.lang.Enum類,所以就不能給一個枚舉類型指定其他父類型了,但是可以讓這個枚舉類型去實現其他接口。
//在枚舉類型中,對接口中的方法進行統--實現
public enum Gender implements Action {
MALE,FEMALE;
public void doSomething(){
System.out.println("接口中抽象方法的統一實現");
}
}
interface Action{
void doSomething();
}
main:
Gender g = null;
g = Gender.MALE;
g.doSomething();
g = Gender.FEMALE;
g.doSomething();
//在每個對象中,分別對這個接口中的抽象方法進行實現
public enum Gender implements Action {
MALE(){
public void doSomething(){
System.out.println("MALE方法的單獨實現");
}
},
FEMALE(){
public void doSomething(){
System.out.println("FEMALE方法的單獨實現");
}
};
}
interface Action{
void doSomething();
}
main:
Gender g = null;
g = Gender.MALE;
g.doSomething();
g = Gender.FEMALE;
g.doSomething();
枚舉類型就是提前在類中定義好了對象的名稱和個數的java類
枚舉例子
public class EnumTest {
public static void main(String[] args){
int mode = (int)(Math.random()*4);//0 1 2 3
EnumTest x = new EnumTest();
x.start(mode);
}
private enum Mode{
MODE_0("模式0","點火"),
MODE_1("模式1","一檔"),
MODE_2("模式2","二檔"),
MODE_3("模式3","三檔");
private String name;//名字
private String msg;//信息
private Mode(String name,String msg){
this.name = name;
this.msg = msg;
}
public String toString(){
return name + ":" + msg;
}
}
public void start(int mode){
Mode m = Mode.valueOf("MODE_"+mode);
System.out.println(m);
}
}
發紙牌案例
Card.java
package com.zzb.day34;
import java.util.*;
public class Card {
public enum Rank{
DEUCE,THREE,FOUR,FIVE,
SIX,SEVEN,EIGHT,NINE,TEN,
JACK,QUEEN,KING,ACE;
}
public enum Suit{
CLUBS,DIAMONDS,HEARTS,SPADES;
}
private final Rank rank;
private final Suit suit;
private static final List<Card> protoDect = new ArrayList<Card>();
static{
for(Suit suit:Suit.values()){
for(Rank rank:Rank.values()){
protoDect.add(new Card(rank,suit));
}
}
}
public Card(Rank rank,Suit suit){
this.rank = rank;
this.suit = suit;
}
public Rank rank(){
return rank;
}
public Suit suit(){
return suit;
}
public String toString(){
return rank + " of " + suit;
}
public static List<Card> newDeck(){
//把原始的牌複製一份作爲一副新牌再返回
List<Card> list = new ArrayList<>(protoDect);
return list;
}
}
Deal.java
package com.zzb.day34;
import java.util.*;
public class Deal {
public static void main(String[] args){
int numHands = 3;//三個人
int cardsPerHand = 5;//一人五張牌
List<Card> list = Card.newDeck();
//洗牌
Collections.shuffle(list);
for(int i=0;i<numHands;i++){
List<Card> hand = deal(list,cardsPerHand);
System.out.println(hand);
}
//System.out.println(list);
System.out.println("發牌結束,還剩:"+list.size()+"張沒發");
}
private static List<Card> deal(List<Card> list,int n){
int size = list.size();
List<Card> handView = list.subList(size-n,size);
List<Card> hand = new ArrayList<>(handView);
handView.clear();
return hand;
}
}
交通燈案例
public enum TrafficLight implements java.io.Serializable {
RED(30){
public TrafficLight next(){
return GREEN;
}
},
YELLOW(5){
public TrafficLight next(){
return RED;
}
},
GREEN(40){
public TrafficLight next(){
return YELLOW;
}
};
private final int duration;
private TrafficLight(int duration){
this.duration = duration;//時間間隔
}
public int getDuration(){
return duration;
}
public abstract TrafficLight next();
public static void main(String[] args){
for(TrafficLight light : TrafficLight.values()){
System.out.println("當前的燈爲:"+light+"燈");
System.out.println("\t持續時間爲"+light.getDuration()+"秒,
等一下是:"+light.next().name()+"燈");
System.out.println();
}
}
}