JavaSE学习笔记——泛型、枚举类、注解

Day11

  • 泛型
    • 什么是泛型?
    • 泛型类
    • 泛型接口
    • 泛型方法
    • 通配符 ?
    • 有限制的通配符
  • 枚举类
    • 手动实现
    • 使用enum关键字实现
    • 枚举类的属性
    • 实现接口的枚举类
    • 枚举类的方法
  • Annotation注解
    • 基本的Annotation
    • 自定义Annotation

泛型

什么是泛型?

JavaSE学习笔记——泛型、枚举类、注解_第1张图片

  • 泛型,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。
  • Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,避免了强制类型转换的麻烦。
  • Java中的泛型,只在编译阶段有效,泛型信息不会进入到运行时阶段。
package generic;

import java.util.*;
/*
 * 泛型示例: 以ArrayList为例
 * 泛型: 用于解决安全问题, 避免了强制转换的麻烦
 */
public class GenericDemo {
	public static void main(String[] args) {
		ArrayList<String> al = new ArrayList<String>();
		al.add("abc_1");
		al.add("abc_2");
		al.add("abc_3");
		
		Iterator<String> it = al.iterator();
		while(it.hasNext()) {
//			String s = (String)it.next();
			String s = it.next();
			System.out.println(s);
		}
	}
}

泛型类

什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,可以使用泛型类来完成。

泛型类上定义的泛型T,可以被类内的各个部分使用。

  • 对象实例化时不指定泛型,默认为:Object。
  • 泛型不同的引用不能相互赋值。
  • 创建类时声明属性或方法的泛型为T(任意取名字),调用时为T赋实际类型,再次使用时,只能传入或接收T类型的数据。

示例1:

package generic;
//泛型类
class Generic<T> {
	private T key;

	public void setKey(T key) {
		this.key = key;
	}
	
	public T getKey() {
		return key;
	}
	
}

public class Test {
	public static void main(String[] args) {
		Generic<String> gen1 = new Generic<String>();
		gen1.setKey("XiaoMing");
		
		Generic<String> gen2 = new Generic<String>();
		gen2.setKey("LiHua");
		
		String str = gen1.getKey();
		System.out.println(str);
		
		Generic<Integer> gen3 = new Generic<Integer>();
		gen3.setKey(22);
		
		int value = gen3.getKey();
		System.out.println(value);
		
		//对象有多个属性时,用名称1.属性 = 名称2.属性, 将2的该属性的值赋给1的这个属性
		//直接用 名称1 = 名称2 赋值时,将2的全部属性都赋值给1
		//这里的名称1和名称2是同种类(泛型类时泛型也要相同)的实例对象。
		gen1 = gen2;
		System.out.println(gen1.getKey());
		
//		gen1 = gen3;//错误,gen1是String类型的类,gen3是Integer类型的类,
		            //虽然都是Generic类的实例对象,但是泛型不同,不能互相赋值
	}

}

注意:同一个类,但是在new()对象时指定的泛型类型不同,这些对象不能相互赋值。

示例2:

package generic;
/**
 * 泛型类
 * 什么时候定义泛型类?
 * 当类中要操作的引用数据类型不确定的时候, 可以使用泛型类来完成.
 * 
 * 泛型类上定义的泛型T, 可以被类内的各个部分使用.
 * 
 * @author 14251
 *
 */
class GenericClassDemo<T>{
	private String id;
	private T info;
	GenericClassDemo(String id, T info){
		this.id = id;
		this.info = info;
	}
	public void setId(String id) {
		this.id = id;
	}
	public void setInfo(T info) {
		this.info = info;
	}
	public String getId() {
		return this.id;
	}
	public T getInfo() {
		return this.info;
	}
	public void printInfo() {
		System.out.println("id: " + this.id + " info: " + this.info);
	}

}

class Person{
	private String name;
	private int age;
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}

}

public class GenericClass {
	public static void main(String[] args) {
		//可以指定泛型为各种引用数据类型
		new GenericClassDemo<String>("LiHua", "技术").printInfo();
		new GenericClassDemo<Integer>("XiaoMing", 1).printInfo();
		new GenericClassDemo<Person>("LiHua", new Person("XiaoHong", 20)).printInfo();
	}
}

泛型接口

1. 定义方式:

//定义一个泛型接口
interface Generator<T> {
	T next();
}

2. 接口未传入泛型实参时,实现类也要声明成泛型类(类的泛型与接口的泛型相同)。

3. 接口传入了泛型实参时,实现类可以不声明成泛型类(类内相关属性以及方法的类型应与接口的泛型类型相同)。

package generic;

interface GenericInterface<T> {
	void show(T t);
}

//类实现接口时指定泛型
class ImpInter1 implements GenericInterface<String>{
	public void show(String s) {
		System.out.println(s);
	}
}

//类实现接口时, 不指定泛型
class ImpInter2<T> implements GenericInterface<T>{
	public void show(T t) {
		System.out.println(t);
	}
}

public class TestInterface{
	public static void main(String[] args) {
		ImpInter1 imp1 = new ImpInter1();
		imp1.show("lalala");
		ImpInter2<Character> imp2 = new ImpInter2<Character>();
		imp2.show('a');
	}
}

泛型方法

什么时候定义泛型方法?

  1. 类内的某个方法操作的引用数据类型不确定时,可以将该方法定义为泛型方法。
  2. 泛型类内可以有与类的泛型不同的泛型方法。

注意:
静态方法不可以访问类上定义的泛型,如需使用泛型,必须定义在方法上。

示例:

package generic;
/**
 * 1. 类内的某个方法操作的引用数据类型不确定时, 可以将该方法定义为泛型方法
 * 2. 泛型类内可以有与类的泛型不同的泛型方法
 * 
 * 注意:
 * 静态方法不可以访问类上定义的泛型, 如需使用泛型, 必须定义在方法上
 * @author 14251
 *
 */
//泛型类内定义不同于类的泛型的泛型
class GenericMethodDemo<T>{//T是类的泛型, 因此类内所有的T都是相同的
	public void showInfo(T t) {//与类的泛型相同
		System.out.println("show: " + t);
	}
	public <Q> void printInfo(Q q) {//定义特有的泛型, Q是局部的, 在该方法外出现的Q与该方法的不同
		System.out.println("print: " + q);
	}
	
	public static <M> void method(M m) {//泛型不能声明为T(T为类上定义的泛型, 必须使用泛型时需要定义在方法上(M))
		System.out.println("method: " + m);
	}
	
}
public class GenericMethod {
	public static void main(String[] args) {
		//方法的泛型与类相同
		new GenericMethodDemo<String>().showInfo("lalala");
		new GenericMethodDemo<Integer>().showInfo(4);
		//方法的泛型与类不同
		GenericMethodDemo<String> g = new GenericMethodDemo<String>();
		g.printInfo(8);//传入与类的泛型不同的参数, 调用泛型方法时, 不需要用<>指定泛型
		g.printInfo("xixixi");
		//静态泛型
		GenericMethodDemo.method("hahaha");
		
	}
}

泛型方法与可变参数

/**
 * 泛型方法与可变参数
 * @param args
 */
public<T> void printMsg(T... args){
    for(T t : args){
	System.out.println(t);
    }
}

通配符 ?

以集合为形参的方法,在不确定集合中的数据类型时,可以使用通配符。

package generic;

import java.util.*;

public class TongPeiFu {
	public static void main(String[] args) {
		List<Integer> list1 = new ArrayList<Integer>();
		List<String> list2 = new ArrayList<String>();
		
		list1.add(1);
		list1.add(3);
		list2.add("x");
		list2.add("y");
		
		TongPeiFu t = new TongPeiFu();
		t.printInfo(list1);
		t.printInfo(list2);
		
	}
	
	public void printInfo(List<?> list) { //任何中的的表都可以传入,可以规定只有String类型可以传入(List list)
		System.out.println(list);
	}
}

有限制的通配符


  • (无穷小 , Person],只允许泛型为Person及Person子类的引用调用


  • [Person , 无穷大), 只允许泛型为Person及Person父类的引用调用


  • 只允许泛型为实 现Comparable接口的实现类的引用调用

package generic;

import java.util.*;
/**
 * 通配符?
 * @author 14251
 * 
 */
class People implements Comparable<People>{
	private String name;
	private int age;
	People(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	public int compareTo(People p) {
		int num = this.getName().compareTo(p.getName());
		if(num == 0) {
			Integer i1 = this.getAge();
			Integer i2= p.getAge();
			num = i1.compareTo(i2);
		}
		return num;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}

class Worker extends People{
	private String factory;
	Worker(String name, int age, String factory){
		super(name, age);
		this.factory = factory;
	}
	public String getFactory() {
		return factory;
	}
	public void setFactory(String factory) {
		this.factory = factory;
	}
	
}

class Teacher extends People{
	private String school;
	Teacher(String name, int age, String school){
		super(name, age);
		this.school = school;
	}
	public String getSchool() {
		return school;
	}
	public void setSchool(String school) {
		this.school = school;
	}
	
}
//比较器, 降序排列
class Com implements Comparator<People>{//该比较器可以对存储了People类以及其子类的集合排序
	public int compare(People o1, People o2) {
		int num = o2.getName().compareTo(o1.getName());
		if(num == 0) {
			Integer i1 = o2.getAge();
			Integer i2= o1.getAge();
			num = i2.compareTo(i1);
		}
		return num;
	}
	
}

public class WildCard {
	public static void main(String[] args) {
		TreeSet<People> tp = new TreeSet<People>(new Com());//People以及其子类都可以传入
		//创建集合对象时, 泛型必须明确
//		ArrayList al = new ArrayList();
//		ArrayList al = new ArrayList();//默认子类可以传入
		tp.add(new People("CB", 18));
		tp.add(new People("AC", 20));
		tp.add(new People("AB", 19));
		tp.add(new People("AC", 20));
		
		TreeSet<Worker> tw = new TreeSet<Worker>(new Com());
		tw.add(new Worker("C-B", 18, "-a-"));
		tw.add(new Worker("A-C", 20, "-b-"));
		tw.add(new Worker("A-B", 19, "-c-"));
		tw.add(new Worker("A-C", 20, "-b-"));
		
		TreeSet<Teacher> tt = new TreeSet<Teacher>();
		tt.add(new Teacher("C=B", 18, "=a="));
		tt.add(new Teacher("A=C", 20, "=b="));
		tt.add(new Teacher("A=B", 19, "=c="));
		tt.add(new Teacher("A=C", 20, "=b="));
		
		printOut(tp);
		System.out.println("--------------------------------");
		printOut(tw);
		System.out.println("--------------------------------");
		printOut(tt);

		
	}
	//迭代器
	public static void printOut(Set<? extends People> s) {//只能打印People以及其子类的集合
		Iterator<? extends People> it = s.iterator();
		while(it.hasNext()) {
			Object obj = it.next();
			if(obj instanceof Worker) {
				Worker w = (Worker)obj;
				System.out.println("name: "+w.getName()+" age: "+w.getAge()+" factory: "+w.getFactory());
			}else if(obj instanceof Teacher) {
				Teacher t = (Teacher)obj;
				System.out.println("name: "+t.getName()+" age: "+t.getAge()+" school: "+t.getSchool());
			}else {
				People p = (People)obj;
				System.out.println("name: "+p.getName()+" age: "+p.getAge());
			}
		}
	}
}

枚举类

在某些情况下,一个类的对象是有限而且固定的。例如季节类,只有 4 个对象,这时就可以使用枚举类。

手动实现

  • 构造器私有化——使用private修饰
  • 属性声明为私有常量——使用 private final 修饰
  • 把该类的所有实例都声明为全局常量——使用 public static final 来修饰

注意:实质是单例设计模式。

package enumexample;

public class Season {
	private final String SEASON;
	private final String DESCRIPTION;
	
	private Season(String SEASON, String DESCRIPTION) {
		this.SEASON = SEASON;
		this.DESCRIPTION = DESCRIPTION;
	}
	
	private static final Season SPRING = new Season("spring", "春天");
	private static final Season SUMMER = new Season("summer", "夏天");
	private static final Season AUTUMN = new Season("autumn", "秋天");
	private static final Season WINTER = new Season("winter", "冬天");
	
	public static Season getSpring() {
		return SPRING;
	}
	
	public static Season getSummer() {
		return SUMMER;
	}
	
	public static Season getAutumn() {
		return AUTUMN;
	}
	
	public static Season getWinter() {
		return WINTER;
	}
	
	public void showInfo() {
		System.out.println(this.SEASON + " " + this.DESCRIPTION);
	}
	
}
package enumexample;

public class Test {
	public static void main(String[] args) {
		Season.getSpring().showInfo();
		Season.getSummer().showInfo();
		Season.getAutumn().showInfo();
		Season.getWinter().showInfo();
	}
	
}

使用enum关键字实现

  • 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类。
  • 枚举类的构造器只能使用 private 访问控制符。
  • 枚举类的所有实例必须在枚举类中显式列出 ( , 分隔 ; 结尾),列出的实例系统会自动添加 public static final 修饰。
  • 所有的枚举类都提供了一个 values 方法,该方法可以很方便地遍历所有的枚举值。
  • 可以在 switch 表达式中使用枚举类的对象作为表达式,case 子句可以直接使用枚举值(实例名)的名字,无需添加枚举类作为限定。
  • 若枚举只有一个成员,则可以作为一种单例模式的实现方式。
  • 与手动实现相比,免除了实例对象时要添加public static final修饰符和new过程,并且可以直接通过 类名.实例 调用对象,不需要通过get()方法取得对象。
package enumexample;

public enum SeasonEnum {
	//调用私有构造,实例化枚举对象
	SRING("spring", "春天"),
	SUMMER("summer", "夏天"),
	AUTUMN("autumn", "秋天"),
	WINTER("winter", "冬天");
	
	private final String SEASON;
	private final String DESCRIPTION;
	
	private SeasonEnum(String SEASON, String DESCRIPTION) {
		this.SEASON = SEASON;
		this.DESCRIPTION = DESCRIPTION;
	}
	
	public void showInfo() {
		System.out.println(this.SEASON + " " + this.DESCRIPTION);
	}
	
}
package enumexample;

public class Test {
	public static void main(String[] args) {
//		SeasonEnum s = SeasonEnum.SRING;
//		s.showInfo();
		
		SeasonEnum.SRING.showInfo();
		SeasonEnum.SUMMER.showInfo();
		SeasonEnum.AUTUMN.showInfo();
		SeasonEnum.WINTER.showInfo();
	}
	
}

枚举类的属性

  • 枚举类对象的属性不允许被改动, 所以应该使用 private final 修饰。
  • 枚举类使用 private final 修饰的属性应该在构造器中为其赋值(常量显示赋值)。
  • 若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的传入参数。

实现接口的枚举类

  • 和普通 Java 类一样枚举类可以实现一个或多个接口。
  • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式, 则可以让每个枚举值分别来实现该方法。
package enumexample;

public interface EnumInterface {
	void printInfo();
}
package enumexample;

public enum SeasonEnum implements EnumInterface{
	//调用私有构造,实例化枚举对象
	SPRING("spring", "春天"),
	SUMMER("summer", "夏天"),
	AUTUMN("autumn", "秋天"),
	WINTER("winter", "冬天");
	
	private final String SEASON;
	private final String DESCRIPTION;
	
	private SeasonEnum(String SEASON, String DESCRIPTION) {
		this.SEASON = SEASON;
		this.DESCRIPTION = DESCRIPTION;
	}
	
	public void showInfo() {
		System.out.println(this.SEASON + " " + this.DESCRIPTION);
	}

	@Override
	public void printInfo() {
		System.out.println(this.SEASON + " " + this.DESCRIPTION);
	}
	
}
package enumexample;

public class Test {
	public static void main(String[] args) {
	
		SeasonEnum.SPRING.showInfo();
		SeasonEnum.SUMMER.showInfo();
		SeasonEnum.AUTUMN.showInfo();
		SeasonEnum.WINTER.showInfo();
		
	}
	
}

枚举类的方法

JavaSE学习笔记——泛型、枚举类、注解_第2张图片

Annotation注解

  • 从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注释)。
  • Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理. 通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。
  • Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation 的 “name=value” 对中。
  • Annotation 能被用来为程序元素(类, 方法, 成员变量等) 设置元数据。

基本的Annotation

  • 使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用. 用于修饰它支持的程序元素。
  • 三个基本的 Annotation:
    @Override:限定重写父类方法, 该注释只能用于方法
    @Deprecated:用于表示某个程序元素(类, 方法等)已过时
    @SuppressWarnings:抑制编译器警告.

自定义Annotation

  • 定义新的 Annotation 类型使用 @interface 关键字。
  • Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明. 其方法名和返回值定义了该成员的名字和类型。
  • 可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字。
  • 没有成员定义的 Annotation 称为标记; 包含成员变量的 Annotation 称为元数据 Annotation。

你可能感兴趣的:(java)