个人推荐:
📢📢📢 前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下
"通俗易懂,风趣幽默"
,感觉非常有意思,忍不住分享一下给大家。
点击跳转到教程。
前言
最近在回顾JavaSE部分的知识,对一些薄弱的知识进行记录,学习方式,通过视频和图书的进行学习,视频看B站韩顺平老师的30天学会Java,图书看Java核心技术 卷I 基础知识(原书第10版)。
韩顺平30天学会地址:Javahttps://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.999.0.0
Java核心技术 卷I 基础知识(原书第10版)
一.泛型的理解和好处
1.泛型的理解
(1) 使用传统方法的问题分析
创建三个类Cat,People,Test类,具体代码如下:
package com.dudu;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Test {
public static void main(String[] args) {
List list =new ArrayList();
list.add(new Cat(1, "嘟嘟"));
list.add(new Cat(2, "小白"));
list.add(new Cat(3, "小花猫"));
list.add(new People(4, "铲屎官"));
for(Object olist:list){
// 向下转型
if (olist instanceof Cat) {
Cat cat = (Cat)olist;
System.out.println(cat.toString());
}else if (olist instanceof People) {
People people =(People)olist;
System.out.println(people.toString());
}
}
}
}
class Cat{
private int id;
private String name;
public Cat(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Cat [id=" + id + ", name=" + name + "]";
}
}
class People{
private int id;
private String name;
public People(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "People [id=" + id + ", name=" + name + "]";
}
}
运行效果:
上面的代码能跑,但是存在一些问题:
在遍历集合的时候,对于集合中的数据是通过判断实例类的方式执行不同向下转型,但是如果传入的是一个不确定的数据类型的时候,就无法获取该实例的属性方法,并且如果强制进行向下转型还会引发错误,所以这种方式既不安全,查询效率也低。
- 不能对加入集合ArrayList中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合的数据较大,对效率有影响
(2) 使用泛型方式
//泛型基本语法:类名<T> 对象名 =new 类名/子类名<>(); // T为引用数据类型
使用泛型后,对集合中的数据进行限定后,不是该限定的数据类型,是无法加入到集合中去的,遍历的时候无需进行类型转换(编译的时候会自动识别该泛型的数据类型)。
2.泛型的好处
- 编译时,检查添加元素的类型,提高了安全性
- 减少了类型的转换次数,提高效率
- 不再提示编译警告
二.泛型介绍
1.什么是泛型
传入的数据类型的数据类型,意思就是泛型是一种数据类型,这个数据类型需要编译的时候传入相应的数据类型,泛型可以限定类/接口的数据类型,可以在类/接口声明时通过一个标识表示类/接口中某个属性的类型(接口不行)
,或者是某个方法的返回值的类型
,或者是参数类型
,是一种动态的数据类型。
代码:
package com.dudu;
public class Test2 {
public static void main(String[] args) {
GenericparaDigmpor<String,Integer,Boolean> genericparaDigmpor= new GenericparaDigmpor<>("张三",18,true);
System.out.println(genericparaDigmpor.toString());
GenericparaDigmpor<Integer,String,String> genericparaDigmpor2 =new GenericparaDigmpor<>(18,"张三","李四");
System.out.println(genericparaDigmpor2.toString());
}
}
class GenericparaDigmpor<T,M,G>{
// 属性的数据类型
T t;
M m;
G g;
// 方法的返回数据类型
public M getM() {
return m;
}
// 参数的数据类型
public void name(G g) {
System.out.println(g.getClass().getSimpleName());
}
public GenericparaDigmpor(T t,M m, G g){
this.t = t;
this.m = m;
this.g = g;
}
public GenericparaDigmpor() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "GenericparaDigmpor [t=" + t.getClass().getSimpleName() +
", m=" + m.getClass().getSimpleName() + ","
+ " g=" + g.getClass().getSimpleName() + "]";
}
}
运行效果:
2.泛型的语法
- 泛型的声明
interface 接口<T>{}
和 class 类<K,T,V,...>{}
说明:
(1)其中,T.K,V不代表值,而是表示类型(泛型标识符)。
(2)任意字母都可以(一般为大写字母)。常用T表示,是Type的缩写。
(3)....表示泛型标识符可以有多个
- 泛型的实例化
类名<泛型的数据类型> 对象名 = new 类名/子类名<泛型的数据类型>();
List<String> strList = new ArrayList<String>();
3.泛型使用细节
- 泛型的使用形式(JavaSE7及其以后版本,可以使用省略写法)
类名<泛型的数据类型> 对象名 = new 类名/子类名<>();
List<String> strList = new ArrayList<>();
- 有泛型限定的类中(如集合类),不加任何泛型修饰,默认使用数据类型为Object的泛型
List list =new ArrayList();
相当于 List<object> list =new ArrayList<>();
未使用泛型:
使用泛型:
注意: List<Object> list = new ArrayList<>();
不要写成 List list = new ArrayList();
这种形式。
- 泛型的数据类型是引用数据类型,对于一些基本数据类型,需要装箱为包装类
当泛型的数据类型为基本数据类型时,会报错!
虽然传入的是int但是实际上会装箱为Integer
上面的代码类似于:
- 在指定泛型具体类型后,可以传入该数据或者其子类类型(多态)
import java.util.List;
public class Test3 {
public static void main(String[] args) {
List<Animal> list =new ArrayList<>();
// 传入该数据
list.add(new Animal());
// 传入该数据的子类对象
list.add(new Cat_animal());
// 传入该数据的子类对象的子类对象
list.add(new DuDu());
//list.add(new Alien());
}
}
class Animal{
public Animal() {
// TODO Auto-generated constructor stub
System.out.println(this.getClass().getSimpleName());
}
}
class Cat_animal extends Animal{
}
class DuDu extends Cat_animal{
}
class Alien {
}
运行效果:
三.自定义泛型
1.自定义泛型类
自定义泛型类就是自己定义的一个带泛型的类,在传统类名后面加上<T,R,...>
语法:
class 类名<T,R...>{
// 成员
}
注意细节:
- 普通成员(属性,方法)可以使用泛型(
前面已经说了
) - 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型) (
前面已经说了
) - 如果在创建对象时,没有指定类型默认为Object(
前面已经说了
)
使用泛型的数组,不能初始化:
静态方法,属性中不能使用类中的泛型标识符(静态在类加载的时候加载,执行在对象的前面,泛型是在创建对象的时候数据类型才确定,所以在静态中无法使用
):
2.自定义泛型接口
自定义泛型接口就是自己定义的一个带泛型的接口,在传统接口名后面加上<T,R,...>
语法:
interface 接口名<T,R...>{
}
代码:
package com.dudu;
public class Test5 {
public static void main(String[] args) {
TigerSum tigerSum = new TigerSumImple();
tigerSum.method1("你好");
tigerSum.method3(2.6);
}
}
// 泛型接口
interface Tiger_Two<T,M,G> {
void method1(T t);
M method2();
G method3(G g);
}
interface TigerSum extends Tiger_Two<String, String, Double>{
}
class TigerSumImple implements TigerSum{
@Override
public void method1(String t) {
// TODO Auto-generated method stub
System.out.println(t.getClass().getSimpleName());
}
@Override
public String method2() {
// TODO Auto-generated method stub
return null;
}
@Override
public Double method3(Double g) {
// TODO Auto-generated method stub
System.out.println(g.getClass().getSimpleName());
return null;
}
}
运行效果:
注意细节:
- 接口中,静态成员(属性)也不能使用泛型(这个和泛型类规定一样)
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定类型,默认为Object
接口中的属性为静态属性,不能使用泛型
泛型接口的类型,在实现接口时确定:
泛型接口的类型,在继承接口时确定:
泛型接口默认泛型类型为Object:
3.自定义泛型方法
语法:
修饰符 <T,R..> 返回类型 方法名(参数列表){
}
注意: 一般泛型方法的参数列表都会有该泛型方法的泛型数据类型,不然泛型方法的数据类型采用的是默认数据类型Object。
代码:
package com.dudu;
public class Test6 {
public static void main(String[] args) {
XiaoHu<String> xiaoHu =new XiaoHu<>();
xiaoHu.method1();
xiaoHu.method2("你好");
xiaoHu.method2(new XiaoHu<String>());
xiaoHu.method3("你好");
xiaoHu.method3("Hello World!");
}
}
// 泛型类中可以使用泛型方法
class XiaoHu<G>{
// 这是一个泛型方法,但是未使用该泛型参数(一般会使用)
public <K> void method1() {
System.out.println("这是一个泛型方法");
}
public <T> void method2(T t) {
System.out.println("这是一个泛型方法,传入的泛型数据类型为:"+t.getClass().getSimpleName());
}
public void method3(G g) {
System.out.println("这不是泛型方法,传入的泛型数据类型为:"+g.getClass().getSimpleName());
}
}
// 接口中可以使用泛型方法
interface XiaoHong{
<T> void method1();
<G> void method2(G g);
}
// 普通类中可以使用泛型方法
class XiaoMing{
public <G> void method1() {
System.out.println("这是一个泛型方法");
}
public <T> void method2(T t) {
System.out.println("这是一个泛型方法,传入的泛型数据类型为:"+t.getClass().getSimpleName());
}
}
运行效果:
注意细节:
- 泛型方法,可以定义在普通类中,也可以定义在泛型类/接口中
- 当泛型方法被调用时,类型会确定
- 参数为泛型的方法不一定是泛型方法(方法的泛型标识符可能来自于类上的标识)
四.通配符
1.泛型继承性
泛型不具有继承性,但可以接受具有继承关系类的值
ArrayList后面的 <>
要么不填(使用省略写法),要么就要填入和前面一致的数据类型!
泛型可以接受具有继承关系类的值(多态)
2.通配符
- <?> 支持任意泛型类型
- <? extends A>:支持A类及其A类的子类,规定了泛型的上限(A类对象)
- <? super A>:支持A类及其A类的父类,不限于直接父类,规定了泛型的下限(A类对象)
代码:
package com.dudu;
import java.util.ArrayList;
import java.util.List;
public class Test8 {
public static void main(String[] args) {
M m =new M();
m.method1(new ArrayList<String>());
m.method2(new ArrayList<A>());
m.method2(new ArrayList<A_Son>());
//m.method2(new ArrayList<A_Super>());
m.method3(new ArrayList<A>());
//m.method3(new ArrayList<A_Son>());
m.method3(new ArrayList<A_Super>());
}
}
class M{
public void method1(List<?> list) {
System.out.println("支持任意泛型类型");
}
public void method2(List<? extends A> list) {
System.out.println("支持A类及其A类的子类");
}
public void method3(List<? super A> list) {
System.out.println("支持A类及其A类的父类");
}
}
class A_Super{
public A_Super(){
System.out.println(this.getClass().getSimpleName());
}
}
class A extends A_Super{
public A(){
System.out.println(this.getClass().getSimpleName());
}
}
class A_Son extends A{
public A_Son(){
System.out.println(this.getClass().getSimpleName());
}
}
运行效果:
如果要传入一个带泛型的集合对象的时候 List<T>
无法直接作为参数的数据类型
此时就可以使用 List<?>
:
并且我们还可以对 <?>
中的 ?
使用 <? extends A>
和 <? super A>
进行限定。
如:<? extends A>
:支持A类及其A类的子类; <? super A>
:支持A类及其A类的父类: