Java SE 053 泛型详解

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

Java SE 053 泛型详解

1.HashSet底层是利用HashMap去实现的,HashMap底层是用数组实现的

2.Properties类

(1)属性(Properties)是HashTable的一个子类。它用来保持值的列表,在其中关键字和值都是字符串(String)。Properties类被其他的Java类所使用。例如,当获得系统环境时,System.getProperties()返回对象的类型。

package com.javase.properties;

import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

public class PropertiesTest {
    
    
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
    
    
		Properties p = System.getProperties();
		Set set = p.keySet();
		for(Iterator iter = set.iterator(); iter.hasNext();){
    
    
			String key = (String)iter.next();
			String value = p.getProperty(key);
			System.out.println(key + "=" + value);
		}
	}
}

在这里插入图片描述
在这里插入图片描述

3.泛型

3.1泛型要解决的问题

值放进去的时候没有问题,但是取出来时就会发生ClassCastException泛型就是为了解决这个问题的,编译的时候只要不出问题,运行的时候一定不会出问题。

package com.javase.generics;

import java.util.ArrayList;
import java.util.List;
/**
 * 值放进去的时候没有问题,但是取出来时就会发生ClassCastException
 * 泛型就是为了解决这个问题的,编译的时候只要不出问题,运行的时候一定不会出问题。
 */
public class ArrayListTest {
    
    
	public static void main(String[] args) {
    
    
		List list = new ArrayList();
		list.add("string");
		list.add(new Integer(2));
		list.add(new Boolean(false));
		
		String str = (String)list.get(0);
		Integer in = (Integer)list.get(1);
		String b = (String)list.get(2);		
	}
}

在这里插入图片描述

4.泛型的使用

(1)T并不是java里面的一个类,它叫做泛型,即很广泛的一种类型。
(2)T并不代表一个具体数值本身,它代表的是一些类型的信息,比如说传一个String,String就是一种类型,传一个Integer实例化的时候,T是代表的是类型的信息,需要给它提供类型的信息。即在实例化类时要传递一个具体的类型。

package com.javase.generics;
public class GenericFoo<T> {
    
    
	private T foo;
	
	public T getFoo() {
    
    
		return foo;
	}

	public void setFoo(T foo) {
    
    
		this.foo = foo;
	}

	public static void main(String[] args) {
    
    
		GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
		GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
		foo1.setFoo(new Boolean(true));
		foo2.setFoo(new Integer(2));
		
		Boolean b = foo1.getFoo();
		Integer integer = foo2.getFoo();
		
		System.out.println(b+" "+integer);
 	}
}

5.泛型的定义与使用

5.1泛型定义在类上

(1)泛型类:把泛型定义在类上,用户在使用该类的时候,才清楚该类具体是什么类型。
(2)使用的时候确定了该类的类型,就不用担心强转的问题,也就不用担心的在运行时类型转换异常的问题了。
(3)用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型

package com.javareview.generic;

public class GenericObj<T> {
    
    
	private T obj;

	public T getObj() {
    
    
		return obj;
	}

	public void setObj(T obj) {
    
    
		this.obj = obj;
	}
}

5.2泛型类的使用

package com.javareview.generic;

public class GenericTest {
    
    
	
	public static void main(String[] args) {
    
    
		//s1.在使用时确定泛型类是什么具体类型,此处为String类型
		GenericObj<String> genericObj = new GenericObj<>();
		genericObj.setObj("xiongjie");
		
		//s2.在使用的时候,该类会自动转换成用户指定的类型了
		String name = genericObj.getObj();
		System.out.println(name);
		
		//s3.在使用时确定泛型类是什么具体类型,此处为Integer类型
		GenericObj<Integer> genericInteger = new GenericObj<Integer>();
		genericInteger.setObj(10);
		
		//s4.使用的时候,该类就会自动转换成用户想要使用的类型了
		Integer age = genericInteger.getObj();
		System.out.println(age);
	}
	
}

5.2泛型方法

(1)在类上定义的泛型,在方法中也可以使用
(2)仅仅在某一个方法上需要使用泛型,外界仅仅是关心该方法,不关心类其他的属性,这样的话在整个类上定义泛型,未免就有些大题小作了。

5.2.1定义泛型方法

(1)定义泛型方法
(2)泛型是先定义后使用的

package com.javareview.generic;
public class GenericMethod {
    
    
	//用户传递进泛型方法的是什么类型,方法返回值就是什么类型
	public <T> void show(T t) {
    
    
		System.out.println(t);
	}
}

5.2.2泛型方法的使用

public class GenericMethodTest {
    
    	
	public static void main(String[] args) {
    
    
		GenericMethod gm = new GenericMethod();
		//s1.用户传递进泛型方法的是什么类型,方法返回值就是什么类型
		gm.show("张三");
		gm.show(22);
		gm.show(12.5);
	}	
}

6.泛型类派生出的子类

(1)泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承。
(2)那它是怎么被继承的呢?这里分两种情况

6.1子类明确泛型类的类型参数变量

(1)泛型接口

package com.javareview.generic;
/**
 *  泛型接口 
 * @author xiongjie
 * @param <T>
 */
public interface GenericInterface<T> {
    
    
	public abstract void show(T t);
}

(2)实现泛型接口的类
子类明确泛型类的类型参数变量

package com.javareview.generic.test;
import com.javareview.generic.GenericInterface;
/**
 *  实现泛型接口的类,子类明确泛型类的类型参数变量
 */
public class GenericInterfaceImpl implements GenericInterface<String>{
    
    
	@Override
	public void show(String t) {
    
    
		System.out.println(t);
	}	
}

6.2子类不明确泛型类的类型参数变量

(1)子类明确泛型类的类型参数变量
当子类不清楚泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在子类中需要定义出类型参数变量。

/**
* 当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量 
 */
public class GenericInterfaceImpl<T> implements GenericInterface<T>{
    
    
	@Override
	public void show(T t) {
    
    
		System.out.println(t);
	}
}

6.3测试这两种情况

package com.javareview.generic.test;

import com.javareview.generic.GenericInterface;
import com.javareview.generic.GenericInterfaceImpl1;
import com.javareview.generic.GenericInterfaceImpl2;

public class GenericInterfaceImplTest {
    
    
	public static void main(String[] args) {
    
    
		//s1.测试第一种情况:子类明确泛型类的类型参数变量
		GenericInterface<String> gi1 = new GenericInterfaceImpl1();
		gi1.show("hello");
		
		//s2测试第二种情况:子类不明确泛型类的类型参数变量
		GenericInterface<String> gi2 = new GenericInterfaceImpl2<>();
		gi2.show("100");
	}
}

6.3.1注意

(1)实现类要是重写父类的方法,返回值的类型是要和父类一样的。
(2)类上声明的泛型只对非静态成员有效。

7.类型通配符

7.1为什么需要类型通配符?

(1)现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?
(2)按照我们没有学习泛型之前,我们可能会这样做:

	public void test(List list) {
    
    
		int i = 0; 
		for(; i < list.size();i++ ) {
    
    
			System.out.println(list.get(i));
		}
	}

(3)上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型

7.2不够优美的写法

(1)该test()方法只能遍历装载着Object的集合,不够优美

	泛型中的<Object>并不是像以前那样有继承关系的,也就是说List<Object>和List<String>是毫无关系的
	public void testTpf1(List<Object> list) {
    
    
		int i = 0; 
		for(; i < list.size();i++ ) {
    
    
			System.out.println(list.get(i));
		}
	}

7.3通配符?

(1)?号通配符表示可以匹配任意类型,任意的Java类都可以匹配

	/**
	 * 通配符的写法
	 * @param list
	 */
	public void testTpf(List<?> list) {
    
    
		//list.add("ccc");只能调对象与类型无关的方法,不能调用对象与类型有关的方法,比如说add()方法,它是与对象类型有关的方法
		int i = 0; 
		for(; i < list.size();i++ ) {
    
    
			System.out.println(list.get(i));
		}
	}

(2)现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
(3)只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。
(4)测试代码

	public static void main(String[] args) {
    
    
		GenericTPF genericTpf = new GenericTPF();
		//s1.初始化集合
		List<String> stringList = new ArrayList<String>();
		stringList.add("fff");
		stringList.add("aaa");
		genericTpf.testTpf(stringList);
	}

7.3.1设定通配符上限

(1)首先,我们来看一下设定通配符上限用在哪里
(2)我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做?
我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限

	List<? extends Number>

上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

public static void main(String[] args) {
    
    
		GenericTPF genericTpf = new GenericTPF();
		//s1.初始化集合
		List<String> stringList = new ArrayList<String>();
		stringList.add("fff");
		stringList.add("aaa");
		//s2.测试通配符
		genericTpf.testTpf2(stringList);
		
		//s3.测试设置通配符上限
		List<Integer> integerList = new ArrayList<Integer>();
		integerList.add(1999);
		integerList.add(129);
		integerList.add(1599);
		integerList.add(1939);
		
		List<Float> floatList = new ArrayList<Float>();
		floatList.add(12.0F);
		floatList.add(13.0F);
		floatList.add(14.0F);
		floatList.add(15.0F);
		
		List<Double> doubleList = new ArrayList<Double>();
		doubleList.add(44.3);
		doubleList.add(45.3);
		doubleList.add(42.3);
		
		List<Long> longList = new ArrayList<Long>();
		longList.add(54L);
		longList.add(52L);
		longList.add(14L);
		longList.add(534L);
		
		genericTpf.testTpf3(integerList);
		genericTpf.testTpf3(floatList);
		genericTpf.testTpf3(doubleList);
		genericTpf.testTpf3(longList);
		//genericTpf.testTpf3(stringList); 因为限定了通配符的上限只能是数值类型,所以此处会报错
	}

7.3.2设定通配符的下限

(1)传递进来的只能是Type或Type的父类

	<? super Type>

(2)在使用泛型过程中限定泛型的下限

	public void testTpfSuper() {
    
    
		//s1.在使用泛型过程中限定泛型的下限
		GenericObj<? super List> genericObjList = null;
		//s1.以下代码出错,是因为在使用过程中限定了泛型的下限为List集合,所以在构建集合的时候必须是List或List的超类
		//genericObjList = new GenericObj<ArrayList>();
		genericObjList = new GenericObj<Object>();
	}

8.通配符和泛型方法

(1)大多时候,我们都可以使用泛型方法来代替通配符的

	//s1.使用通配符
	public static void test(List<?> list){
    
    
	
	}
	
	//s2.使用泛型方法
	public <T> void test2(List<T> list){
    
    
	
	}

(2)使用通配符还是使用泛型方法的原则

a.如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法

b.如果没有依赖关系的,就使用通配符,通配符会灵活一些.

9.泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

总结

(1)所谓泛型:就是变量类型的参数化。
(2)解说:不是变量本身,是变量的类型,变量就是一个赋值,它是类型,什么类型,T类型,之前是确定的,现在将其参数化了,只有当构造这个对象的时候才能确定这个T到底是什么类型。
(3)泛型是先定义后使用的

猜你喜欢

转载自blog.csdn.net/xiogjie_67/article/details/108544666