目录
前言
java面向对象高级笔记复盘【已完】,后续更新在常用API(一)中!
一、static
1.1 static修饰成员变量
- 类变量:有static修饰,属于类,在计算机中只有一份,会被类的全部对象所共享
- 实例变量(对象的变量):无static修饰,属于每个对象。
1.static是什么?
static:静态,修饰成员变量、成员方法
2.static修饰成员变量叫什么?怎么使用?有什么特点?
类变量(静态成员变量)
则类名.类变量(推荐),对象名.类变量(不推荐)
3.无static修饰的成员变量叫什么?怎么使用?有什么特点?
实例变量(对象变量)
对象.实例变量
属于对象,每个对象中都有一份
1.2 static的应用场景
在开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改)则该数据可以定义成类变量来记住
1.成员变量有几种?各自在什么情况下定义?
- 类变量:数据只需要一份,且需要被共享式(访问、修改)
- 实例变量:每个对象都要有一份,数据各不同(如:name、score、age)
2.访问自己类中的类变量,是否可以省略类名不屑?
可以的,在某个类中访问其他类里的类变量,必须带类名访问
1.3 static的修饰方法
1.static修饰的成员方法叫什么?如何使用?
- 类方法(静态方法)
- 属于类,可以直接使用类名访问呢,也可以用对象访问
2.无static修饰的成员方法叫什么?如何使用?
- 实例方法(对象的方法)
- 属于对象,只能用对象访问
1.4 static修饰方法的应用场景
1.为什么工具类中的方法要用类方法,而不用实例方法?
- 实例方法需要创建对象来调用,此时对象只是为了调用方法,对象占内存,会浪费内存空间
- 类方法,直接用类名调用,调用方便,节省内存
注:工具类中没有创建对象的需求,建议将工具类的构造器进行私有
2.类方法有哪些应用场景?
- 可以用设计工具类
3.工具类是什么?有什么好处?
- 工具类中的方法都是类方法,每个类方法都是用来完成一个功能
- 提高了代码的复用性;调用方便,提高开发效率
4.为什么工具类要使用类方法,而不是实例方法?
- 实例方法需要创建对象来调用,会浪费内存空间
5.工具类定义有什么要求?
- 工具类不需要创建对象,建议将工具类进行私有化!
1.5 static的注意事项
注意事项:
- 类方法可以访问类成员,不可以访问实例成员
- 实例方法可以访问类成员,也可以访问实例成员
- 实例方法可以出现this关键字,类方法中不可以
1.6 static代码块
1.静态代码块
- 格式:static{}
- 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
- 作用:完成类的初始化,例如:对类变量的初始化赋值
2.实例代码块
- 格式:{}
- 特点:每次创建对象时,都会执行实例代码块,并在构造器前执行
- 作用:和构造器一样,都是用来完成对象的初始化。例如:对实例变量及逆行初始化赋值
1.7 static应用知识(单例设计模式)
1.什么时设计模式?
- 一个问题通常有n中解法,其中肯定有一种解法时最优的,这个最有的解法被人总结出来,称为设计模式
- 设计模式一般来说有20多种,对20多种软件开发中会遇到问题
单例设计模式:确保一个类只有一个对象
写法:
- 把类的构造器私有
- 定义一个类变量记住类的一个对象
- 定义一个类方法,返回对象
1.7.1 饿汉式单例模式
2.单例怎么限额?饿汉式单例的特点是什么?
- 把类的构造器私有;定义一个类变量存储类的一个对象;提供一个类方法返回对象
- 特地:在获取类的对象时,对象已经创建了
3.单例哪些应用场景,有哪些好处?
- 任务管理器对象、获取运行时对象
1.7.2 懒汉式单例模式
特点:拿对象时,才开始创建对象
二、继承
2.1 继承的概述
1.什么时继承?继承后有什么特点?
- 继承就是用extend关键字,让一个类和另一个类建立起一种父子关系
- 子类可以继承父类非私有的成员
2.带继承关系的类,Java会怎么创建它的对象?对象创建出来后,可以直接访问哪些成员?
- 带继承关系的类,Java会用类和父类,这多张设计图来一起创建类的对象
- 对象能直接访问什么成员,是由子父类这多种设计图共同决定,这多种设计图对外暴露了什么成员,对象就可以访问什么成员
2.2 继承有哪些好处
特点:减少重复的代码
2.3 继承相关的注意事项
2.3.1 权限修饰符
用来限制类中成员(成员变量、成员方法、构造器、代码块……)能够被访问的范围
2.3.2 单继承与object类
2.3.1.1 单继承
java时单继承方式,一个类只能继承一个直接父类,java中的类不支持多继承,但支持多层继承
2.3.1.2 Object类
- object类时java所有类的祖宗类,写任何一个类时,其实都是object的子类或者子孙类
- Java创建时,没有继承任何类时,会默认继承object类(object隐藏)
2.3.3 方法重写
1.什么时方法重写?
- 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称,参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
- 注:重写后,方法的访问,Java会遵循就近原则
2.方法重写的注意事项
- 重写小技巧:使用@Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
- 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错的。
3.方法重写在开发中常见的应用场景
当子类觉得父类的方法不好用,或者不满足自己的需求时,就可以用方法重写
- 子类重写Object类的toString()方法,以便返回对象的内容
2.3.4 子类中访问其他成员的特点
1.在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
2.如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?
- 可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
2.3.5 子类构造器的特点
子类的全部构造器,都会先调用父类的构造器,再执行自己,其中父类如果是无参的构造器有个默认的super(),子类构造函数里面也有个默认的super()调用的方法
1.怎么将父类无参构造器取消?
就需要在父类中添加一个有参的构造器-》子类就会报错(解决方法:在子类中添加super()调用父类的有参构造器就不会报错了)
结论:
- 子类的全部构造器,都会先调用父类的构造器,再执行自己
- 默认情况下,子类全部构造器的第一行代码都是 super()(写不写都有),它会调用父类的无参数构造器
- 如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(...),指定去调用父类的有参数构造器。
2.3.6 子类构造器的应用场景
补充知识:
- this(..)调用兄弟构造器
- 任意类的构造器中,都可以通过this(...)去调用该类的其他构造器。
总结:
1.子类构造器有啥特点?
子类中的全部构造器,都必须先调用父类的构造器,再执行自己
2.super(...)调用父类有参数构造器的常见应用场景是什么?
为对象中包含父类这部分的成员变量进行赋值
3.this(...)的作用是什么?
在构造器中调用本类的其他构造器
4.this(...)和super(...)的使用需要注意什么?
都必须放在构造器的第一行
三、多态
3.1 多态的概述
1.什么是多态?
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态
2.多态的前提
有继承/实现关系;存在父类引用子类对象;存在方法重写
3.多态的注意事项
多态是对象、行为的多态,Java中的属性(成员变量)不谈多态
3.2 使用多态的优点
- 在多态形式下,右边对象是解耦合的,更便于扩展和维护
- 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利
1.使用多态有什么好处?存在什么问题?
- 可以解耦合,扩展性更强;使用父类类型的变量作为方法的形参时,可以接收一切子类对象
- 多态下不能直接调用子类的独有方法
2.类型转换有几种形式?能解决什么问题?
- 自动类型转换、强制类型转换
- 可以把对象转换成其真正的类型,从而解决了多态下不能调用子类独有方法的问题
3.强制类型转换需要注意什么?
- 存在继承/实现时,就可以进行强制类型转换,编译阶段不会报错
- 但是,运行时,如果发现对象的真实类型与强转后的类型不同会报错(ClassCastException)
4.强制类型转换前?Java建议我们做什么事情?
- 使用instanceof判断当前对象的真实类型:对象instanceof类型
四、final关键字
- final关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:该类被称为最终类,特点是不能被继承了
- 修饰方法:该方法被称为最终方法,特点是不能被重写了
- 修饰变量:该变量只能被赋值一次
注:
- final修饰基本类型的变量,变量存储的数据不能被改变
- final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的
4.1 补充知识:常量
- 使用了 static final修饰的成员变量就被称为常量
- 作用:通常用于记录系统的配置信息
使用常量的优点:
- 代码可读性更好,可维护性也更好
- 程序编译后,常量会被“宏替换”,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
五、抽象类
- 在java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法
- abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
注:
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有
- 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
1、抽象类、抽象方法是什么样的?
- 都是用abstract修饰的;抽象方法只有方法签名,不能写方法体
2抽象类有哪些注意事项和特点?
- 抽象类中可以不写抽象方法,但有抽象方法的类一定是抽象类
- 类有的成员(成员变量、方法、构造器)抽象类都具备
- 抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
3.抽象类的应用场景和好处是什么?
两种主要的应用场景
- (1)用抽象类,我们可以把子类中相同的代码,包括方法签名都抽上来,这样能更好的支持多态,以提高代码的灵活性。
- (2)反过来用,我们不知道系统未来具体的业务实现时,我们可以先定义抽象类,将来让子类去继承实现,以方便系统的扩展。
5.1 模板设计方法的写法
1.定义一个抽象类
2.里面定义两个方法
- 一个是模板方法:把相同代码放里面去。
- 一个是抽象方法:具体实现交给子类完成
建议使用final关键字修饰模板方法,为什么?
- 模板方法是给对象直接使用的,不能被子类重写。
- 一旦子类重写了模板方法,模板方法就失效了。
六、接口
6.1 接口的概述
Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构:接口
- 注:接口不能创建对象;接口是用来被类实现(implements)的,实现接口的类称为实现类
- 实现类实现多个接口,必须重写完全部接口的全部抽象方法:一个类可以实现多个接口(接口可以理解成干爹),方法,否则实现类需要定义成抽象类。
6.2 使用接口的好处
- 弥补了类单继承的不足,一个类同时可以实现多个接口
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现
1.使用接口有啥好处,第一个好处是什么?
- 可以解决类单继承的问题,通过接口,我们可以让一个类有一个亲爹的同时,还可以找多个干爹去扩展自己的功能。
2.为什么我们要通过接口,也就是去找干爹,来扩展自己的功能呢?
- 因为通过接口去找干爹,别人通过你implements的接口,就可以显性的知道你是谁,从而也就可以放心的把你当作谁来用了。
3.使用接口的第二个好处是什么?
- 一个类我们说可以实现多个接口,同样,一个接口也可以被多个类实现的。这样做的好处是我们的程序就可以面向接口编程了,这样我们程序员就可以很方便的灵活切换各种业务实现了。
6.3 接口的综合案例
1.创建一个Student类
public class Student {
private String name;
private char sex;
private double score;
public Student() {
}
public Student(String name, char sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
2.创建一个ClassManager类
import java.util.ArrayList;
public class ClassManager {
private ArrayList<Student> students=new ArrayList<>();
private StudentOperator studentOperator=new StudentOperatorImpt2();
public ClassManager(){
students.add(new Student("迪丽热巴",'女',99));
students.add(new Student("古力娜扎",'女',100));
students.add(new Student("故而扎哈",'男',80));
students.add(new Student("卡尔扎巴",'男',60));
}
//打印全班的学生信息
public void printInfo(){
studentOperator.printAllInfo(students);
}
//打印全班全部学生的平均分
public void printScore(){
studentOperator.printAverageScore(students);
}
}
3.创建一个StudentOperator接口
import java.util.ArrayList;
public interface StudentOperator {
void printAllInfo(ArrayList<Student> students);
void printAverageScore(ArrayList<Student> students);
}
4.创建一个StudentOperatorImpt1类
import java.util.ArrayList;
public class StudentOperatorImpt1 implements StudentOperator{
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("-------------全部学生信息如下:---------------------");
int count1=0;
int count2=0;
for (int i = 0; i <students.size() ; i++) {
Student s=students.get(i);
System.out.println("姓名:"+s.getName()+"性别:"+s.getSex()+"成绩:"+s.getScore());
if(s.getSex()=='男'){
count1++;
}else {
count2++;
}
}
System.out.println("男:"+count1+'\t'+"女:"+count2);
System.out.println("班级总人数为:"+students.size());
System.out.println("---------------------------------------");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore=0.0;
double max= students.get(0).getScore();
double min= students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student s=students.get(i);
if(s.getScore()>max) max=s.getScore();
if(s.getScore()<min) min=s.getScore();
allScore+=s.getScore();
}
System.out.println("学生的最高分是:"+max);
System.out.println("学生的最低分是:"+min);
System.out.println("平均分是:"+(allScore-max-min)/(students.size()-2));
}
}
5. 在创建一个StudentOperatorImpt2类
import java.util.ArrayList;
public class StudentOperatorImpt2 implements StudentOperator{
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("-------------全部学生信息如下:---------------------");
int count1=0;
int count2=0;
for (int i = 0; i <students.size() ; i++) {
Student s=students.get(i);
System.out.println("姓名:"+s.getName()+"性别:"+s.getSex()+"成绩:"+s.getScore());
if(s.getSex()=='男'){
count1++;
}else {
count2++;
}
}
System.out.println("男:"+count1+'\t'+"女:"+count2);
System.out.println("班级总人数为:"+students.size());
System.out.println("---------------------------------------");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore=0.0;
double max= students.get(0).getScore();
double min= students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student s=students.get(i);
if(s.getScore()>max) max=s.getScore();
if(s.getScore()<min) min=s.getScore();
allScore+=s.getScore();
}
System.out.println("学生的最高分是:"+max);
System.out.println("学生的最低分是:"+min);
System.out.println("平均分是:"+(allScore-max-min)/(students.size()-2));
}
}
6.创建一个Test类
public class Test {
public static void main(String[] args) {
ClassManager classManager=new ClassManager();
classManager.printInfo();
classManager.printScore();
}
}
6.4 JDK8开始、新增的方法
1.JDK8开始,接口中新增了哪些方法?
- 默认方法:使用default修饰,使用实现类的对象调用。
- 静态方法:static修饰,必须用当前接口名调用
- 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
- 他们都会默认被public修饰。
6.5 接口的多继承和注意事项
- 接口多继承的作用:便于实现类去实现
-
package interface_attention; public class Test { } interface A{ void test1(); } interface B{ void test2(); } interface C{} //接口多继承 interface D extends A,B,C{} class E implements D{ @Override public void test1() { } @Override public void test2() { } }
注意事项(了解):
1.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
4.一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可
package interface_attention;
public class Test {
public static void main(String[] args) {
Zi zi=new Zi();
zi.run();
}
}
//1.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
interface I {
void test1();
}
interface J {
String test1();
}
/*interface K extends I,J{
}*/
//2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
/*class C implements I,J{
}*/
//3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
class Fu{
public void run(){
System.out.println("==父类的run方法执行了===");
}
}
interface IT{
default void run(){
System.out.println("接口中的run方法执行了");
}
}
class Zi extends Fu implements IT{
}
//4.一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可
interface It1{
default void test(){
System.out.println("IT1");
}
}
interface It2{
default void test(){
System.out.println("IT1");
}
}
class N implements It1,It2{
@Override
public void test() {
System.out.println("自己的方法");;
}
}
七、内部类
7.1 内部类的概述
- 是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类
- 场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类
内部类包含:成员内部类、静态内部类、局部内部类、匿名内部类
7.2 成员内部类
就是类中的一个普通成员,类似前面我们学过的普通的成员变量、成员方法
Outer.class
package com.itheima.de_inner_class1;
public class Outer {
private int age = 99;
public static String a;
//成员内部类
public class Inner {
private String name;
public static String schoolName;//JDK 16才开始支持定义静态成员的
private int age = 88;
public void test() {
System.out.println(age);
System.out.println(a);
int age = 66;
System.out.println(age);//66
System.out.println(this.age);//88
System.out.println(Outer.this.age);//99 使用Outer.this.age来获取外部类的数据
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Test.class
package com.itheima.de_inner_class1;
public class Test {
public static void main(String[] args) {
Outer.Inner in=new Outer().new Inner();//先new外部类,在new内部类
in.test();
}
}
1.成员内部类是什么?如何创建其对象?
- 就是类中的一个普通成员,类似前面我们学过的普通成员变量、成员方法
- 外部类名.内部类名 对象名=new 外部类(...).new 内部类(...);
2.成员内部类的实例方法中,访问其他成员有啥特点?
- 可以直接访问外部类的实例成员、静态成员
- 可以拿到当前外部类对象,格式是:外部类名.this。
7.3 静态内部类
特点:可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员
Outer.class
package com.itheima.de_inner_class2;
public class Outer {
private int age;
public static String schoolName;
//静态内部类
public static class Inner {
private String name;
public void test() {
System.out.println(schoolName);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void test2(){
System.out.println(schoolName);//可以访问静态内部类的静态成员,不可以访问静态内部类的实例成员(对象成员)
}
}
Test.class
package com.itheima.de_inner_class2;
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.test();
}
}
1.什么是静态内部类?如何创建对象?有啥特点?
- 有static修饰的内部类
- 外部类名.内部类名 对象名 =new 外部类.内部类(...);
- 可以直接访问外部类的静态成员,不能直接访问外部类的实例成员
7.4 匿名内部类
- 就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字。
- 特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象
- 作用:用于更方便的创建一个子类对象
Cat.class
package com.itheima.de_inner_class3;
public class Cat extends Animal {
@Override
void cry() {
System.out.println("jiao");
}
}
Animal.class
package com.itheima.de_inner_class3;
abstract class Animal {
abstract void cry();
}
Test.class
package com.itheima.de_inner_class3;
public class Test {
public static void main(String[] args) {
/* Animal animal = new Cat();
animal.cry();*/
//把这个匿名类翻译成一个子类,然后立即创建一个子类的对象出来
Animal a = new Animal() {
@Override
void cry() {
System.out.println("jiao");
}
};
a.cry();
}
}
7.4.1 匿名内部类常见的使用场景
通常作为一个参数传输给方法
package de_inner_class4;
public class Test {
public static void main(String[] args) {
/* Swimming s1= new Swimming() {
@Override
public void swimming() {
System.out.println("狗游泳飞快");
}
};
go(s1);*/
go(new Swimming() {
@Override
public void swimming() {
System.out.println("狗游泳飞快");
}
});
}
//设计一个方法,可以接收swimming接口的一切实现类对象来参加游泳比赛
public static void go(Swimming s) {
System.out.println("开始------------------------");
s.swimming();
}
}
//猫和狗都要参加游泳比赛
interface Swimming {
void swimming();
}
1.匿名内部类的书写格式是什么样的 ?
2.匿名内部类有啥特点 ?
- 匿名内部类本质就是一个子类,并会立即创建出一个子类对象
3.匿名内部类有啥作用、应用场景?
- 可以更方便的创建出一个子类对象。
八、枚举
枚举是一种特殊的类
- 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
- 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
- 枚举都是最终类,不可以被继承。
- 枚举类中,从第二行开始,可以定义类的其他各种成员。
- 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。
enum类
package com.itheima.de_enum;
public enum A {
// 枚举类里面都是一些常量,记住每一个对象
X,Y,Z;
}
Test类
package com.itheima.de_enum;
public class Test {
public static void main(String[] args) {
A a1=A.X;
A a2=A.Y;
A a3=A.Z;
}
}
8.1 枚举常见的应用场景
- 用来表示一组信息,然后作为参数进行传输。
- 选择定义一个一个的常量来表示一组信息,并作为参数传输
- 参数值不受约束
- 选择定义枚举表示一组信息,并作为参数传输
- 代码可读性好,参数值得到了约束,对使用者更友好,建议使用!
Enum.class
package com.itheima.de_enum;
public enum Constant {
BOY,
GIRL;
}
Test.class
package com.itheima.de_enum;
public class Test2 {
public static void main(String[] args) {
provideInfo(Constant.GIRL);
}
public static void provideInfo(Constant sex) {
switch (sex){
case BOY:
System.out.println("I M BOY");
break;
case GIRL:
System.out.println("I M GIRL");
break;
}
}
}
九、泛型
9.1 泛型的概述
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口,泛型方法、它们统称为泛型
也可以技能泛型的继承,但是必须是定义所及继承的类型
9.2 泛型接口
注意:类型变量建议用大写的英文字母,常用的有:E、T、K、V等
Student.class(空项)
Class.class(空项)
Data.interface
package com.itheima.generics_interface;
public interface Data<T>{
void add(T t);
void get(T name);
}
TeatcherData.class
package com.itheima.generics_interface;
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public void get(Teacher name) {
}
}
StudentData.class
package com.itheima.generics_interface;
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public void get(Student name) {
}
}
同理,上述泛型也可以进行继承其他的类,使用的时候必须是继承他的父类或者被继承的子类
9.3 泛型方法
Test.class
package com.itheima.generics_method;
public class Test {
public static void main(String[] args) {
String rs= test("java");
System.out.println(rs);
}
public static <T> T test(T t) {
return t;
}
}
有比如 新建一个Car.class ,BMW 、BENZ类,其中都是空项目
在Test类中实现通配符和泛型指定的继承类型
通配符包括上限继承和下限继承
package com.itheima.generics_method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new BENZ());
go(cars);
ArrayList<BMW> bmws = new ArrayList<>();
cars.add(new BMW());
cars.add(new BMW());
go(bmws);
ArrayList<BENZ> benzs = new ArrayList<>();
cars.add(new BENZ());
cars.add(new BENZ());
go(benzs);
}
/*
//仅仅可以接收Car类下的集合
public static <T extends Car> void go(ArrayList<T> cars){
}*/
//? 通配符,使用泛型的时候可以代表一切类型
//? 通配符也可以继承car仅能接受car的子类 ?extends Car(上限继承) ? super Car (下限继承)
public static void go(ArrayList<? extends Car> cars) {
}
}
9.4 泛型的注意事项
- 泛型是工作在编译阶段的,一日程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除
- 泛型不支持基本数据类型,只能支持对象类型(引用数据类型)