Java SE 学习之继承,抽象,代码块,接口,多态,内部类
1知识点
1.1继承
1.1.1引入
- 根据上面的案例,老师类和学生类有相同的属性,相同的方法放,具有共性,向上抽取,关键字extend,特点 :子类可以直接使用父类中非私有的成员
1.1.2优点
- 提高代码的复用性,维护性,并且让类与类产生了联系,是多态的前提
1.1.3缺点
- 继承是侵入性的,降低代码的灵活性:子类必须拥有父类非私有的属性和方法
增强了代码的耦合性
1.1.4使用场景
- 1 有共性 2:产生了is a 的关系 :例子 我是帅哥 ,你也是帅哥 ,提取帅哥为父类,
1.1.5继承特点
- 只支持单继承和多层继承(爷祖)
1.1.6继承的成员变量访问特点
- 就近原则
public class one {
public static void main(String[] args) {
zi zi = new zi();
zi.mothed1();
zi.mothed2();
zi.mothed3();
}
}
//fu类
class fu {
int a = 10;
}
//子类
class zi extends fu {
int a = 20;
public void mothed1() {
System.out.println(a+"就近原则");
}
public void mothed2() {
int a =30;
System.out.println(this.a+"this关键字返回本类成员变量");
System.out.println(a+"局部变量");
}
public void mothed3() {
System.out.println(super.a+"super返回父类");
}
}
1.1.6.1 this and super
- this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
- this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 成员变量:
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
1.1.7继承的成员方法访问特点
- 先去子类找 ,没有再去父类找
public class Two {
public static void main(String[] args) {
zi2 zi = new zi2();
zi.mothed2();
zi.mothed3();
}
}
//fu类
class fu2 {
//fu类方法
public void mothed() {
System.out.println("fu2类方法");
}
}
//子类
class zi2 extends fu2 {
//子类与父类同名方法
public void mothed() {
System.out.println("zi2类方法");
}
//子类中的方法通过super调用父类同名方法
public void mothed2() {
super.mothed();
}
// 默认有个this. -- 调用子类方法
public void mothed3() {
mothed();
}
}
1.1.8方法的重写
- 1、方法重写概念
- 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
- 2、方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
//需求 升级手机功能
public class Three {
public static void main(String[] args) {
ipear2 i =new ipear2();
i.smallBlack();
}
}
//ipear1类-----一代
class ipear1 {
//打电话
public void call(String name) {
System.out.println("给" + name + "打电话");
}
//只会说英文
public void smallBlack() {
System.out.println("speak English");
}
}
//二代
class ipear2 extends ipear1 {
//进行重写
public void smallBlack() {
//调用父类的方法
super.smallBlack();
//添加自己的方法
System.out.println("speak Chinese");
}
}
1.1.8.1方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能低于父类的访问权限
- 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法,子类只是将父类的方法隐藏起来了
1.1.9权限修饰符

1.1.10继承中构造方法的访问特点(idea自动生成)

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法
注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
注意:super 会在堆内存开辟自己的存储空间来存放父类成员
1.2抽象类
1.2.1概述
- 抽象方法:父类中的某些方法不能明确具体实现方式,就需要定义成抽象方法,子类要重写父类中的抽象方法(完成方法体内容的编写)
- 抽象类:一个类中存在抽象方法,则该类必须声明为抽象类
- 如何定义抽象类和抽象方法
abstract关键字
//抽象类的定义
public abstract class 类名 {}
//抽象方法的定义
public abstract void eat();
1.2.2抽象类的注意事项
-
抽象类中可以没有抽象方法,有抽象方法的类一定是抽象类
-
抽象类不能实例化(实例化就是创建对象)
-
抽象类可以有构造方法
- 但是无法创建对象
- 构造方法主要用于初始化自己成员变量,以供子类使用
-
抽象类的子类
- 要么重写父类所有的抽象方法(推荐)
- 要么自己也是一个抽象类(工作中可能会用到)
1.2.3模板设计模式
- 把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法
让使用模板的类(继承模板抽象类的子类)去重写抽象方法实现需求,下面是一个写作文的模板
package Day02;
public abstract class Composition {
//模板
public void write() {
System.out.println("《我的dady》");
body();
System.out.println("哈哈哈");
}
//抽象主题方法
public abstract void body();
}
//创建子类重写抽象方法
class Tom extends Composition {
@Override
public void body() {
System.out.println("俺不会写作文");
}
}
//创建子类对象,调用父类方法
class test {
public static void main(String[] args) {
Tom tom=new Tom();
tom.write();
}
}
1.2.4 finall关键字
- 根据模板设计思想,模板的骨架最好不被重写,
- finall-最终
- 被其修饰的方法无法被子类重写
- 被其修饰的类不能拥有子类
- 被其修饰的变量不能再被赋值:基本数据类型–值 ;引用数据类型:地址值
- 如果要修饰成员变量,要注意初始化时机:
1.在定义final修饰的变量(常量)的时候, 直接赋值(推荐)2. 在构造方法结束之前(白话:就是在构造方法中), 完成赋值
package com.itheima.mfinal;
public class TestFinal {
/*
final修饰变量:
基本数据类型变量: 其值不能被更改
引用数据类型变量: 地址值不能被更改, 但是可以修改对象的属性值
*/
public static void main(String[] args) {
// 常量的命名规范: 如果是一个单词, 所有字母大写, 如果是多个单词, 所有字母大写, 但是中间需要使用_分隔
final int A = 10;//A变量的值是不能被修改
// a = 10;
final int MAX = 10;
final int MAX_VALUE = 20;
final Student stu = new Student();
stu.setName("张三");
stu.setName("李四");
// stu = new Student();
}
}
class Student {
// final修饰成员变量 初始化时机
// 1. 在创建的时候, 直接给值
// 2. 在构造方法结束之前, 完成赋值
final int a = 10;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.3代码块
- 引出:系统调试不方便
- 目的:每当程序启动完毕以后,系统就初始化一部分学生数据
- 分类:局部代码块,构造代码块,静态代码块
1.3.1 局部代码块
- 位置:方法中定义
- 作用:限定变量的生命周期,及早释放,提高内存利用率
- 示例代码
public class Test {
/*
局部代码块
位置:方法中定义
作用:限定变量的生命周期,及早释放,提高内存利用率
*/
public static void main(String[] args) {
{
int a = 10;
System.out.println(a);
}
// System.out.println(a);
}
}
1.3.2 构造代码块
- 位置:类中方法外定义
- 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
- 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
- 示例代码
public class Test {
/*
构造代码块:
位置:类中方法外定义
特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
*/
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student(10);
}
}
class Student {
{
System.out.println("好好学习");
}
public Student(){
System.out.println("空参数构造方法");
}
public Student(int a){
System.out.println("带参数构造方法...........");
}
}
1.3.3 静态代码块
- 位置:类中方法外定义
- 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
- 作用:在类加载的时候做一些数据初始化的操作
- 示例代码
public class Test {
/*
静态代码块:
位置:类中方法外定义
特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
作用:在类加载的时候做一些数据初始化的操作
*/
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person(10);
}
}
class Person {
static {
System.out.println("我是静态代码块, 我执行了");
}
public Person(){
System.out.println("我是Person类的空参数构造方法");
}
public Person(int a){
System.out.println("我是Person类的带...........参数构造方法");
}
}
1.4接口
1.4.1接口的引出
- 根据2.2.4.5的改进,出现了一个全是抽象方法的类,
- 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
1.4.2接口的意义
- 用来定义规范
- 用来做功能的拓展
1.4.3接口的特点
- 接口用关键字interface修饰
public interface 接口名 {}
- 类实现接口用implements表示
public class 类名 implements 接口名 {}
接口不能实例化,我们可以创建接口的实现类对象使用
- 接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
1.4.4接口中成员特点
- 成员变量
只能是常量
默认修饰符:public static final - 构造方法
没有,因为接口主要是扩展功能的,而没有具体存在 - 成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性- JDK8:用default可以定义非抽象方法,用static定义静态方法,静态方法通过类名 . 的方式调用,注意default和static只能有一个
- JDK9:允许接口中定义private私有的方法,(private和static会吗,默认带上default,反正就是不用写default)
1.4.4类与接口的关系
- 类与类的关系
继承关系,只能单继承,但是可以多层继承 - 类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(亲爹更亲,类和接口有一样的方法,执行类中的方法) - 接口与接口的关系
继承关系,可以单继承,也可以多继承(如果继承的接口有重名且方法体不一样的方法,需要重写该方法)
1.5多态
1.5.1多态的概念
同一个对象,在不同时刻表现出来的不同形态
1.5.2多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
1.5.3多态中成员访问的特点
-
成员访问特点
-
成员变量
编译看父类,运行看父类
-
成员方法
编译看父类,运行看子类,父类中要有方法,不然会编译失败,运行则运行子类中重写的方法
-
代码展示
-
class Fu {
int num = 10;
public void method(){
System.out.println("Fu.. method");
}
}
class Zi extends Fu {
int num = 20;
public void method(){
System.out.println("Zi.. method");
}
}
public class Test2Polymorpic {
/*
多态的成员访问特点:
成员变量: 编译看左边 (父类), 运行看左边 (父类)
成员方法: 编译看左边 (父类), 运行看右边 (子类)
*/
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);
f.method();
}
}
1.5.4多态的好处与弊端
- 好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作 - 弊端
不能使用子类的特有成员,因为要先编译,如果父类中没有调用的方法,编译会失败
1.5.5多态的转型及其风险
-
向上转型
父类引用指向子类对象就是向上转型
-
向下转型
格式:子类型 对象名 = (子类型)父类引用;
1.5.5多态的风险(解决向下转型的风险)
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
- 解决方案
- 关键字
instanceof - 使用格式
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
- 关键字
//父类 动物
abstract class Animal {
public abstract void eat();
}
//狗继承了动物类 ,除了吃还能看家
class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
}
// 猫也继承了动物
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
//测试类
public class Test4Polymorpic {
public static void main(String[] args) {
//创建了一条狗,调用方法
useAnimal(new Dog());
//创建了一只猫,调用方法
useAnimal(new Cat());
}
public static void useAnimal(Animal a){
// Animal a = new Dog();
// Animal a = new Cat();
//这个方法可以调用父类动物所有子类对象
a.eat();
//a.watchHome();
// Dog dog = (Dog) a;
// dog.watchHome(); // ClassCastException 类型转换异常
// 判断a变量记录的类型, 是否是Dog
if(a instanceof Dog){
Dog dog = (Dog) a;
dog.watchHome();
}
}
}
1.6内部类
1.6.1 概念
- 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
- 成员内部类:可以被private和state修饰
- 局部内部类
- 匿名内部类
1.6.2成员内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
- 以下是成员内部类的 演示代码
//外部类
public class Four {
public static void main(String[] args) {
Outer.Inner i = new Outer().new Inner();
//外部类想要访问内部类 ,必须要创建内部类对象
System.out.println(i.num);
i.show();
}
}
//外部类
class Outer {
//外部类私有的值
private int a = 20;
//成员内部类
public class Inner {
int num = 10;
//内部类里面的方法
public void show() {
//可以直接访问,内部类访问外部类
System.out.println(a);
System.out.println("hh");
}
}
}
- 成员内部类被pivate修饰 ,需要在创建一下方法,方法中调用私有的方法
public class Five {
public static void main(String[] args) {
//创建外部类对象
Outer1 i = new Outer1();
//调用方法
i.mothed();
}
}
//外部类
class Outer1 {
//外部类私有的值
private int a = 20;
//私有成员内部类
private class Inner {
int num = 10;
//内部类里面的方法
public void show() {
//可以直接访问,内部类访问外部类
System.out.println(a);
System.out.println("hh");
System.out.println("内部类私有方法");
}
}
//创建方法调用私有内部类
public void mothed() {
Inner i = new Inner();
i.show();
}
}
- 成员内部类被state修饰,调用正常方法和state方法
- 访问格式为:Outer2.Inner i = new Outer2.Inner();少一个括号和new
public class Six {
public static void main(String[] args) {
//创建静态类对象
Outer2.Inner i = new Outer2.Inner();
//调用静态类非state方法
i.show();
//调用静态类中静态的方法
Outer2.Inner.mothed();
}
}
//外部类
class Outer2 {
//私有成员内部类
static class Inner {
//内部类里面的方法
public void show() {
System.out.println("我是静态内部类");
}
public static void mothed() {
System.out.println("我是静态内部类中静态的方法");
}
}
}
1.6.3局部内部类
- 定义:在方法中定义的类,外界无法直接使用,需要在方法内部创建对象并使用
- 可以访问外部类中的成员,也可以访内部类中的局部变量
package Day3_31;
public class Seven {
public static void main(String[] args) {
//创建外部类对象
Outer3 outer3=new Outer3();
//调用外部类的方法
outer3.mothed();
}
}
//外部类
class Outer3 {
//成员变量
int a =10 ;
//创建方法
public void mothed() {
//在方法中有个类 ,称之为局部内部类
class Inner {
//局部变量
int b =20 ;
//类中存在方法
public void show() {
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("我是局部内部类中的方法");
}
}
//在外部类中创建具备内部类的对象
Inner I = new Inner();
//调用方法
I.show();
}
}
1.6.4匿名内部类
- 本质上是个特殊的局部内部类(定义在方法内部)
- 前提:需要存在一个接口或者类
- 目的:把继承|实现,方法重写,创建对象放在了一部进行
一般接口实现过程:1. 创建实现类2. 重写接口的方法3. 创建实现类对象4. 调用实现类对象的方法 - 匿名内部类:一部完成, 若接口存在多个方法,使用父类接口来接收,来调用方法,父类引用指向子类对象 :多态
package TextInter;
public class Eight {
public static void main(String[] args) {
// 1. 创建实现类
// 2. 重写接口的方法
// 3. 创建实现类对象
// 4. 调用实现类对象的方法
//创建对象
InterImpl ii = new InterImpl();
//调用方法
ii.show();
new Inter() {
@Override
public void show() {
System.out.println("我是匿名内部类的show方法");
}
}.show();
//一个接口存在多个方法,使用父类接口来接收,来调用方法
//父类引用指向子类对象 :多态
Inter2 inter2 = new Inter2() {
@Override
public void show1() {
System.out.println("我是show1");
}
@Override
public void show2() {
System.out.println("我是show2");
}
};
//调用方法
inter2.show1();
inter2.show2();
}
}
//创造一个接口
interface Inter {
void show();
}
interface Inter2 {
void show1();
void show2();
}
//接口的实现类
class InterImpl implements Inter {
@Override
public void show() {
System.out.println("我是接口show方法的重写");
}
}
1.6.4.1匿名内部类的使用场景
- 当方法的形式参数是接口或者是抽象类时,可以将匿名内部类作为实际参数进行传递
package TextInter2;
public class Nine {
public static void main(String[] args) {
goSwiming(
//匿名内部类创建接口对象
new Swiming() {
@Override
//重写方法
public void swim() {
System.out.println("我们去游泳把");
}
});
}
//创造一个方法调用接口
//方法体是调用里面的show方法
public static void goSwiming(Swiming swiming) {
swiming.swim();
}
}
//创造一个游泳接口
interface Swiming {
void swim();
}
1.6.5 Lambda表达式
- 对匿名内部类的优化,但两者本质上是有区别的
- 函数式思想,怎么做是重点
goSwiming(
//匿名内部类创建接口对象
//lambda表达式
() -> System.out.println("我们去游泳把"));
}
- 标准格式:(形式参数)->{方法体}
- 使用前提:有一个接口,并且有且只有一个抽象方法
无参数无返回值
package Lamble01;
public class Ten {
public static void main(String[] args) {
//匿名内部类方法
useShowHander(new ShowHander() {
@Override
public void show() {
System.out.println("重写方法");
}
});
//lambda表达式的方法
useShowHander(() -> {
System.out.println("重写方法");
});
}
//需要调用接口的方法
public static void useShowHander(ShowHander showHander) {
showHander.show();
}
}
//创建接口
interface ShowHander {
void show();
}
有参数无返回值
package Lambda02;
public class Eleven {
public static void main(String[] args) {
//匿名内部类方法
useShowHander(new ShowHander() {
@Override
public void show(String msg) {
System.out.println("我是匿名内部类" + msg);
}
});
//lambda表达式的方法
useShowHander((String msg) -> {
System.out.println("我是lambda表达式" + msg);
});
}
//需要调用接口的方法
public static void useShowHander(ShowHander showHander) {
showHander.show("手动给出");
}
}
//创建接口
interface ShowHander {
void show(String msg);
}
- 无参数有返回值:需要用return语句将结果返回
- 下面是一个产生随机数的问题
package Lambda03;
import java.util.Random;
public class Twelven {
public static void main(String[] args) {
//匿名内部类方法
useRanNumHandler(new RanNumHandler() {
@Override
public int getNumber() {
//产生随机数
Random random = new Random();
return random.nextInt(10) + 1;
}
});
//lambda方法
useRanNumHandler(() -> {
Random random = new Random();
//返回
return random.nextInt(10) + 1;
});
}
//需要调用接口的方法
public static void useRanNumHandler(RanNumHandler ranNumHandler) {
int number = ranNumHandler.getNumber();
System.out.println(number);
}
}
//创建接口
interface RanNumHandler {
int getNumber();
}
带参数带返回值
package Lambda04;
public class DMOO13 {
public static void main(String[] args) {
useCalculatuor(new Calculatuor() {
@Override
public int calc(int a, int b) {
return a + b;
}
});
useCalculatuor((int a, int b) -> {
return a + b;
});
}
public static void useCalculatuor(Calculatuor calculatuor) {
int calc = calculatuor.calc(10, 20);
System.out.println(calc);
}
}
interface Calculatuor {
int calc(int a, int b);
}
1.6.5.1 Lambda表达式的省略格式
- 参数类型可以省略,但有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,括号可以省略
- 如果代码块的语句只有一个,可以省略 大括号,分好和return
1.6.6 两者的区别
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- 原理 编译之后产生一个单独的。class文件
- lambda表达式:只能是接口,并且接口中只有一个抽象方法
- 字节码文件动态生成,不会产生到本地文件当中
这篇博客深入探讨了Java SE中的核心概念,包括类的继承、抽象类与抽象方法、构造代码块、接口的使用以及多态的实现。讲解了继承的优缺点、方法重写、模板设计模式,同时介绍了final关键字在防止方法和类被重写中的应用。此外,还详细阐述了代码块的作用,如局部、构造和静态代码块,以及内部类的分类和访问特点。最后,文章讨论了接口的定义、实现和多态带来的灵活性,以及匿名内部类和Lambda表达式的应用场景和差异。
1673

被折叠的 条评论
为什么被折叠?



