Java编程基础倒数第二篇,感谢没有放弃的自己。此专栏暂时已完结,接下来就是搞定算法。
学习笔记参考书籍《Java编程基础》主编 张焕生。本书内容比较适合没有什么基础的入门小白,学完一章还有习题,比较适合初学者。
自律、积极、勤奋以及先从一个怎么样都不可能不会实现的小目标开始。
本文已收录于[ 专栏 ]
《Java入门》系列
文章目录前面[ 章节 ]:
Java学习笔记十七——集合类详细总结各自对比
✌ Java必备基础十六——三万字总结输入与输出流
Java必备基础十五——万字长文总结异常处理基本知识点
Java必备基础十四——内部类详细知识点归纳
爛Java必备基础十三——接口详细知识点归纳
✨Java必备基础十二——抽象类
✌Java必备基础十一——多态详解以及多态原则
Java必备基础十——类的继承详解
Java必备基础九——包装类
Java必备基础八——Math类、Random类、日期时间类
Java必备基础七——字符串的基本常用方法
Java必备基础六——一维数组
Java必备基础五——类的封装
盧Java必备基础四——成员变量和局部变量
Java必备基础三——方法的声明与调用,递归
Java必备基础二——类和对象
Java必备基础一——Java语言基础
- 一、泛型是什么
- 1.1泛型的由来
- 1.2 泛型的优点
- 二、泛型的简单使用
- 2.1 泛型菱形语法
- 2.2 泛型简单举例
- 2.3 定义一个泛型类
- 2.3.1 基本写法
- 2.3.2 举个例子
- 2.4 定义一个泛型接口
- 2.4.1 基本写法
- 2.4.2 举个例子
- 2.4 定义一个泛型方法
- 2.5.1 基本写法
- 2.5.2 举个例子
- 2.5.3 总结
- 2.5 泛型通配符
在没有泛型之前,集合中加入特定的对象时,就会被当成Object类型。当从集合中取出对象后,需要进行强制类型转换如何打印相应的对象。这种操作的弊端很容易引起异常,并且代码臃肿。
举个简单的被说了无数遍的例子:
public class Example1_1 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("a");
arrayList.add(1);
for (int i=0;i
输出的结果是:ClassCastException异常
a
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.chapter8.Example1_1.main(Example1_1.java:17)
集合类ArrayList添加了String类型和Integer类型,但是输出时把这两个类型强转了,因此程序崩溃了。为了解决这样的问题 ,引入泛型。
引入泛型的集合可以记住元素类型,在编译时检查元素类型,如果集合中添加了不满足类型的对象时编译器就会提示错误。可以看到下图中定义了泛型元素为String类型,添加int类型的对象时编译器提示要求String类型对象。
需要额外注意的一点是:泛型的集合可以记住元素类型,在编译时检查元素类型,举个例子理解这句话:
public class Example1_1 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList<>();
ArrayList arrayList1 = new ArrayList<>();
arrayList1.add(1);
arrayList.add("2");
Class classStringArrayList = arrayList.getClass();
Class classStringArrayList1 = arrayList1.getClass();
if(classStringArrayList.equals(classStringArrayList1)){
System.out.println("类型相同");
}
}
}
可以猜测一下程序的输出结果,程序会打印出类型相同。虽然两个类添加了不同类型的泛型,但是这两个类依旧是相同的类别。于是我们可以大胆的猜测一下:泛型只在编译阶段有效,在检验泛型结果后,程序会将泛型的相关信息擦除,泛型信息不会进入到运行时阶段。
1.2 泛型的优点
在由来中已经简单提到了没有泛型时的缺点,那么有了泛型时,缺点就是优点:
- 减少代码臃肿,使代码变得简洁
- 消除源代码中许多强制类型转换,提高Java中的类型安全。
二、泛型的简单使用
2.1 泛型菱形语法
List list = new linkedList<>();
Set set = new HashSet<>();
Map map = new HashMap<>();
2.2 泛型简单举例
泛型对于集合类非常重要,在集合中引入泛型能够提供编译时的类型安全,并且从集合中取出元素后不必再强制转换,简化了程序代码。
public class Demo1 {
public static void main(String[] args) {
List ar=new ArrayList<>();
Map map = new HashMap<>();
ar.add("a");
ar.add("b");
map.put(1,"c");
map.put(2,"d");
System.out.println("ar的集合元素为:"+ar);
//遍历Map集合中的元素的一种方法
Iterator> map1=map.entrySet().iterator();
while (map1.hasNext()){
Map.Entry next = map1.next();
System.out.println(next.getKey()+""+next.getValue());
}
}
}
输出的结果为:
ar的集合元素为:[a, b]
1c
2d
2.3 定义一个泛型类
2.3.1 基本写法
class 类名<泛型标识>{
private 泛型标识 标识名
}
2.3.2 举个例子
public class Example1_1{
private T key;
public Example1_1(T key){//泛型构造方法形参key的类型也为T,T的类型也由外部决定
this.key = key;
}
public T getKey() {
return key;
}
public static void main(String[] args) {
//泛型的类型参数只能是引用类型,并且传入的实参类型必须要与定义的泛型对象的类型相同
Example1_1 exampleInt= new Example1_1<>(0);
//错误,定义泛型对象类型要和传入的实参类型相同
Example1_1 exampleInt1= new Example1_1("1");
Example1_1 exampleString = new Example1_1<>("2");
System.out.println("exampleInt的key值:"+exampleInt.getKey());
System.out.println("exampleString的key值:"+exampleString.getKey());
//下面这些写法都是正确的,定义的泛型类不用都传入泛型的实参,实际上的泛型类可以被定义成任何类型,如果有定义,就会像上面一下做出限制
Example1_1 a = new Example1_1(2);
Example1_1 b = new Example1_1("hello");
Example1_1 c = new Example1_1(false);
}
}
输出结果为:
exampleInt的key值:0
exampleString的key值:2
2.4 定义一个泛型接口
2.4.1 基本写法
//定义一个泛型接口
public interface Example1_2 {
T next();
}
2.4.2 举个例子
//定义一个泛型接口Example1_2
public interface Example1_2 {
T fly();
}
//接下来实现这个接口
class Bird implements Example1_2{
@Override
public T fly() {
return null;
}
}
class Bird1 implements Example1_2{
@Override
public T fly() {
return null;
}
}
class Bird2 implements Example1_2{
@Override
public String fly() {
return null;
}
}
2.4 定义一个泛型方法
2.5.1 基本写法
public T genericMethod(Class tClass) throws Exception{
T instance = tClass.newInstance();//newInstance方法抛出了异常
return instance;
}
看到上面的例子说明一下泛型的方法:
- public 与返回值终中间的
,可以李佳节为声明此方法的泛型方法 - 只有声明了
的方法才是泛型方法 - 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
2.5.2 举个例子
✨✨✨下面这个例子概括了很多正确的泛型方法和不正确的泛型方法:自己在电脑上敲一遍,泛型方法就大致ok了。
public class Generic {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public E setKey(E key) {
return key;
}
public T showKeyName(Generic generic) {
return generic.key;
}
public void showKeyName1(Generic generic) {
}
public void showKeyName2(Generic> generic) {
}
public T showKeyName3(Generic generic) {
}
public void showKeyName3(E t) {
}
public void showKeyName4(T t) {
}
public static void showKeyName4(T... args) {
for (T t :args){
System.out.println(t);
}
}
public static void showKeyName5(T t) {
}
}
2.5.3 总结
- 泛型方法能使方法独立于类而产生变化
- 泛型方法前面一定要声明泛型
,除了非静态的void修饰的无返回值方法外,其它都要声明 - 对于静态的无返回值的泛型方法,必须要声明泛型,并且方法的形参的类型一定与声明的泛型保持一致
2.5 泛型通配符
先来看一个例子:
class Bird2 {
public static void showValue(Bird2 bird2) {
System.out.println("这是Integer类型");
}
public static void main(String[] args) {
Bird2 bird2 = new Bird2();
Bird2 bird1 = new Bird2();
showValue(bird2);
showValue(bird1);//这里会报错
}
}
上面的 showValue(bird1)会报错:解决办法就是:将原来的Integer类型修改为?,问题就可以解决。
public static void showValue(Bird2<?> bird2) {
System.out.println(bird2.fly());
类型通配符一般使用?代替具体的类型实参,此处的?是类型实参,不是形参。可以解决诸如上面的当具体类型不确定的时候,使用?代替,在使用的时候给这个类型实参赋真正的类型。



