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();
}
}
}