day07-面向对象高级一
Java面向对象高级一
—————————————————————————————————————————————————————————————————————
1.final
-
final关键字:final是最终的意思,可以修饰类、方法、变量;
修饰类:该类被称为最终类,特点是不能被继承;
修饰方法:该方法被成为最终方法,特点是不能被重写了;
修饰变量:该变量有且仅能被赋值一次。 -
final修饰静态变量,这个变量今后被称为常量,可以记住一个固定值,
并且程序中不能修改了,通常这个值作为系统的配置信息;
常量的名称,建议全部大写,多个单词用下划线链接。 -
变量有哪些?
1、成员变量:
① 静态成员变量
② 实例成员变量
2、③ 局部变量 -
定义和赋值的问题?
① final修饰静态成员变量,必须要先赋值。
② final修饰实例变量(一般没有意义),一定要先赋值。可以在定义时赋值,也可以在构造器或实例初始化块中赋值,但必须确保在构造过程结束之前(即创建对象之前)完成赋值,一旦赋值后不可再修改。
③ final修饰局部变量,可以先只定义,后面再赋值,但是只能赋值一次;必须在定义时或者同一个方法内的后续代码中明确赋值,只能赋值一次。
不能先定义后赋值,除非是在定义的同时进行初始化,或者确保在同一作用域内有确定的赋值操作。 -
final修饰变量注意:
final修饰基本类型的变量,变量存储的数据不能被改变;
final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。(数组,类,接口,String类型……)
public class FinalDemo1 {
// ① final修饰静态成员变量,必须要先赋值。
public static final String SCHOOL_NAME = "XXX大学";
// ② final修饰实例变量(一般没有意义),一定要先赋值。
private final String name = "林青霞";
public static void main(String[] args) {
// ③ final修饰局部变量
final double rate ;
rate = 3.14;
// rate = 3.1415; // 第二次赋值,报错
pay(8);
FinalDemo1 fd = new FinalDemo1();
System.out.println(fd.name); // 林青霞
// f.name = "林志玲"; // 第二次赋值,报错
System.out.println("-------------------");
final int [] arr = new int []{11,22,33,44};
// arr = new int []{22,33,44,55}; // 第二次赋值,报错
// final修饰引用类型的变量,变量存储的地址不能被改变,
// 但地址所指向对象的内容是可以被改变的
arr[2]= 55;
}
// 传入的形参变量值不能被修改,以防止恶意修改
public static void pay(final double z){
// z = 9; // 报错
System.out.println(z);
}
}
// 1. final修饰类:表示该类不能被继承
final class A{
}
//class B extends A{ } // 报错
// 2. final修饰方法:表示该方法不能被重写
class C{
final void show(){
System.out.println("C.m1()");
}
}
class D extends C{
// @Override // 报错,不能重写final方法
//void show(){
// System.out.println("D.m1()");
// }
}
2.单例类
-
单例类:(是Java的一种软件设计模式)
-
什么是设计模式?
一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。 -
关于设计模式,主要学什么?
1、解决什么问题?
2、怎么写? -
单例设计模式:
作用:确保每个类只能创建一个对象。
例如computer上的任务管理器,在Java中是一个JFrame对象(窗口对象),这个任务管理器在电脑上应该只有一个对象,这种就可以设计成单例类,对外就只能创建一个对象。
还有Java的虚拟机对象,也只创建一个。 -
写法、实现步骤:
① 把类的构造器私有;
② 定义一个静态变量(类变量),用于存储对象;
③ 定义一个静态方法(类方法),保证返回的是同一个对象。 -
饿汉式单例:拿对象时,对象早就创建好了。
public class A {
private static final A a = new A();
private A(){
}
public static A getInstance(){
return a;
}
}
- 懒汉式单例:拿对象时,才开始创建对象。
public class B {
private static B b;
private B(){
}
public static B getInstance(){
if(b == null){
b = new B();
}
return b;
}
}
public class Test {
public static void main(String[] args) {
// 目标:设计单例类
A a1 = A.getInstance(); // 获取唯一对象
A a2 = A.getInstance();
System.out.println(a1); // 地址:sun.superstring.singleinstance.A@2f4d3709
System.out.println(a2); // 地址:sun.superstring.singleinstance.A@2f4d3709
System.out.println(a1 == a2); // true
// 这样写不专业,可以随时将A的唯一对象干掉,所以需要写一个get方法,用于返回A类的唯一对象
// A.a = null;
// System.out.println(A.a);
System.out.println("-------------------------");
B b1 = B.getInstance();
B b2 = B.getInstance();
System.out.println(b1); // 地址:sun.superstring.singleinstance.B@1d81eb93
System.out.println(b2); // 地址:sun.superstring.singleinstance.B@1d81eb93
System.out.println(b1 == b2); // true
}
}
// 设计成单例设计模式:饿汉式单例
public class A {
// 2.定义一个静态变量,用于记住本类的一个唯一对象
// public static A a = new A(); 容易被外部修改
// private static A a = new A(); 只能内部改,不暴露出去
private static final A a = new A(); // 推荐
// 加上private,防止外部修改;加上final,防止在内部修改
// 1.私有化构造方法:确保单例类对外不能创建太多对象,单例才有可能性
private A(){
}
// 3.定义一个静态方法,用于获取本类的唯一对象
public static A getInstance(){
return a;
}
}
// 设计成单例设计模式:懒汉式单例
public class B {
// 2.定义一个静态变量,用于存储对象
private static B b; // null
// 1.私有化构造方法:确保单例类对外不能创建太多对象,单例才有可能性
private B(){
}
// 3.提供一个静态方法返回对象,真正需要对象的时候才开始创建对象
public static B getInstance(){
if(b == null){
b = new B();
}
return b;
}
}
3.枚举类
-
枚举类:是一种特殊类
-
枚举类的写法:
枚举类第一行,只能写枚举类的对象名称,且要用逗号隔开;
这些名称,本质是常量,每个常量都记住了枚举类的一个对象。
修饰符 enum 枚举类名{
名称1,名称2,……;
其他成员……;
}
-
枚举类的特点:
枚举类都是最终类,不可以被继承,枚举类都是继承java.lang.Enum类的;
枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量都会记住枚举类的一个对象;
枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象;
所以枚举类是多例模式;
编译器为枚举对象新增了几个方法。枚举类对象名.ordinal():拿到枚举类对象的索引(指代第几个枚举类对象,从0开始) -
多例模式:(对外不能创建对象,又不能被别人继承,里面只有X、Y、Z三个对象,单例是只有一个对象,多例是只能有多个对象)
单例类也可以理解为枚举对象只有一个;
// 枚举类
public enum A {
// 枚举类的第一行:只能罗列枚举对象的名称,这些对象本质是常量
X, Y, Z;
}
public class Test {
public static void main(String[] args) {
// 目标:认识枚举类,搞清楚其本质特点。
A a1 = A.X;
System.out.println(a1); // X
A a2 = A.Y;
System.out.println(a2.toString()); // Y
System.out.println(a1.name()); // X
System.out.println(a2.name()); // Y
System.out.println(a1.ordinal()); // 可以拿枚举对象的索引 0
System.out.println(a2.ordinal()); // 索引 1
}
}
- 枚举类的常见应用场景:
枚举类很适合做信息分类和标志。
数字迷阵的游戏,控制某个图块上下左右的移动。这些方向的信号可以定义成枚举类;
或者是某两个数相除,最终结果保留两位小数,其他位数是:直接舍弃down,进一位up,还是四舍五入half_up
public class Constant {
public static final int UP = 0; // 上
public static final int DOWN = 1; // 下
public static final int LEFT = 2; // 左
public static final int RIGHT = 3; // 右
}
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
public class Test2 {
public static void main(String[] args) {
// 目标:掌握枚举类的应用场景:做信息的分类和标志
// 需求:模拟上下左右移动图片
// 第一种是常量做信息标志和分类:但参数值不受约束
move(Constant.UP); // direction = 0 向上移动
// 第二种是枚举做信息标志和分类:参数值受约束
move(Direction.DOWN); // direction = DOWN 向下移动
}
// 第二种匹配:使用枚举型变量作为信号标志
public static void move(Direction direction){
System.out.println("direction = " + direction);
// 根据这个方向做移动:上下左右
switch (direction){ // switch支持省略枚举对象的前缀:Direction,可以自动去找对应的枚举对象
case UP:
System.out.println("向上移动");
break;
case DOWN:
System.out.println("向下移动");
break;
case Direction.LEFT:
System.out.println("向左移动");
break;
case Direction.RIGHT:
System.out.println("向右移动");
}
}
// 第一种匹配:使用int型变量作为信号标志
public static void move(int direction){
System.out.println("direction = " + direction);
// 根据这个方向做移动:上下左右
switch (direction){
case 0:
System.out.println("向上移动");
break;
case 1:
System.out.println("向下移动");
break;
case 2:
System.out.println("向左移动");
break;
case 3:
System.out.println("向右移动");
break;
default:
System.out.println("输入有误:");
}
}
}
4.抽象类
- 抽象类:
在Java中有一个关键字叫:abstract,就是抽象的意思,用它可以修饰类、成员方法。
abstract修饰类,就是抽象类;
abstract修饰方法,就是抽象方法;
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
}
public abstract class A{
// 抽象方法:必须abstract修饰,只有方法签名,不能有方法体
public abstract void test(int a);
}
- 抽象类的注意事项,特点:
抽象类中不一定要有抽象方法,但是有抽象方法的类必须是抽象类;
类有的成员(成员变量,成员方法,构造器),抽象类都可以有;
抽象类最主要的特点是——抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现;
一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
public class AbstractDemo1 {
public static void main(String[] args) {
// A a = new A(); // 错误,抽象类不能创建对象
// 抽象类的使命就是被子类继承:就是为了生孩子
B b = new B();
b.setName("小明");
b.setAge(18);
System.out.println(b.getName() + ", " + b.getAge()); // 小明 18
b.show(); // B类重写了run方法
b.show1(); // show1方法
}
}
// 抽象类,可以不写抽象方法
public abstract class A {
private String name; // 实例变量
private int age; // 实例变量
// 无参构造器
public A() {
}
// 有参构造器
public A(String name, int age) {
this.name = name;
this.age = age;
}
// 抽象方法:必须用abstract修饰,没有方法体{},只有方法声明
public abstract void show();
// 实例方法
public void show1(){
System.out.println("show1方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 一个类继承抽象类,必须重写抽象类的所有抽象方法,否则这个类也必须定义成抽象类
public class B extends A{
// 重写抽象类的run方法,B就成为了一个具体的类;否则B必须定义成抽象类
@Override
public void show() {
System.out.println("B类重写了run方法");
}
}
-
抽象类不能创建对象的原因:
假设抽象类可以创建对象,那里面的抽象方法怎么执行,抽象方法连方法体都没有(不能抱着抽象类里面不写抽象方法的掉以轻心的心态) -
抽象的本质:不应该有具体的对象
一个类继承抽象类,必须重写抽象类的所有抽象方法,否则这个类也必须定义成抽象类;
抽象类的使命就是被子类继承:就是为了生孩子 -
使用抽象类的好处:
父类知道每个子类都要做某个行为,但为了每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现;我们设计这样的抽象类,就是为了更好地支持多态。 -
父类定义的方法,子类不会用,而是用子类重写的方法,所以父类的方法体定义出来是没有用的,所以这些代码是多余的。
抽象方法,子类是一定要重写的。但是不写成抽象方法也可以实现多态,但是写成抽象方法是一种最佳的实践。
(虽然abstract不是必须的技术,但是这是一种最优雅的写法,是最佳实践)
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
a.shout(); // 汪汪汪~~~
}
}
public abstract class Animal {
// 每个动物的叫声
public abstract void shout();
}
public class Cat extends Animal{
@Override
public void shout() {
System.out.println("猫是喵喵喵~~~");
}
}
public class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪~~~");
}
}
-
抽象类模板方法设计模式:
解决了什么问题?
提供了一个方法作为完成某类功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现。
模板方法设计模式可以提高代码的复用,并简化子类设计。 -
怎么写?
1、定义一个抽象类
2、在里面定义2个方法
一个是模板方法:把共同的实现步骤放进去
一个是抽象方法:不确定的实现步骤,交给具体的子类来完成。 -
多学一招:
建议使用final关键字修饰模板方法:这样子类就不能重写模板方法了,因为模板方法一旦重写,就失效了。
它是给子类使用的,不能被子类重写;
一旦子类重写了模板方法,模板方法就失效了。
public class Test {
public static void main(String[] args) {
// 目标:理解抽象类的使用场景之二:模板方法设计模式
// 学生和老师都要写一篇作文:《我的爸爸》
// 第一段是一样的:我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:
// 第二段是不一样的:老师和学生各自写各自的
// 第三段是一样的:我爸爸真好,你有这样的爸爸吗?
// 解决:抽出一个父类。父类中还抽取一个模板方法给子类直接用。
Teacher teacher = new Teacher();
// People teacher = new Teacher(); 这两个是一个意思
teacher.write();
Student student = new Student();
student.write();
}
}
// 抽象类:老师和学生都是People类
public abstract class People {
// 1、模板方法设计模式
public final void write(){
System.out.println("\t\t\t《我的爸爸》");
System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
// 2、模板方法知道子类一定要写这个正文,但是每个子类写的信息是不同的,父类就定义一个抽象方法
// 具体的事件交给子类来重写正文
writeMain();
System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
}
// 抽象方法 —— writeMain();
public abstract void writeMain();
}
public class Teacher extends People{
@Override
public void writeMain(){
System.out.println("\t我爸爸经常让我站在这里别动,他要去买几斤橘子~~柚子我也吃,毕竟是我爸爸买的,是亲生的!!!");
}
// public void write(){
// System.out.println("\t\t\t《我的爸爸》");
// System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
// System.out.println("\t我爸爸经常让我站在这里别动,他要去买几斤橘子~~柚子我也吃,毕竟是我爸爸买的,是亲生的!!!");
// System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
// }
}
public class Student extends People{
@Override
public void writeMain(){
System.out.println("\t我爸爸很厉害,熟知君子六艺,是一个言行合一的君子,我非常喜欢他,他就是我以后找男朋友的标准!!");
}
// public void write() {
// System.out.println("\t\t\t《我的爸爸》");
// System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
// System.out.println("\t我爸爸很厉害,熟知君子六艺,是一个言行合一的君子,我非常喜欢他,他就是我以后找男朋友的标准!!");
// System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
// }
}
5.接口
- 接口:Java提供了一个关键字interface定义接口;
JDK 8之前,接口中只能写常量和抽象方法——传统接口(本课学习内容就是传统接口)
public interface 接口名{
// 成员变量(常量)
// 成员方法(抽象方法)
}
-
接口的特点:
接口中的抽象方法都是公开的;(写不写public都是公开的)
接口不能创建对象;
接口是用来被类实现(implements)的,实现接口的类称为实现类,一个类可以实现多个接口。(接口可以理解为“干爹”,亲爸继承只能有一个,但是干爹可以有多个。) -
接口的好处:
弥补了类单继承的不足,一个类可以同时实现多个接口,使得类的角色更多,功能更强大;
让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现(更利于程序的解耦合)。
// 接口:使用interface关键字定义的
public interface A {
// JDK 8之前,接口中只能写常量和抽象方法——传统接口
// 1、常量:接口中定义常量可以省略 public static final 不写,默认会加上。
String SCHOOL_NAME = "黑马程序员";
public static final String SCHOOL_NAME2 = "黑马";
// 前面的 public static final 可以省略不写
// 2、抽象方法:接口中定义的方法可以省略 public abstract 不写,默认会加上。
// public abstract void study();
void study();
String go();
}
public interface B {
void play(); // 玩
}
// C 被称为实现类。同时实现了多个接口
// 要么把C定义成一个抽象类 abstract修饰
// 要么C 全部重写A和B的全部抽象方法
class C implements A, B {
@Override
public void study() {
System.out.println("C类重写了study方法");
}
@Override
public String go() {
return "黑马找磊哥";
}
@Override
public void play() {
System.out.println("C类重写了play方法");
}
}
public class Test {
public static void main(String[] args) {
// 目标:认识接口,搞清楚几口的特点,基本使用
System.out.println(A.SCHOOL_NAME); // 黑马程序员
// 注意:接口不能创建对象
// 接口是用来被类实现的
C c = new C();
c.study(); // C类重写了study方法
System.out.println(c.go()); // 黑马找磊哥
c.play(); // C类重写了play方法
}
}
-
案例:
A公司设计一个A接口(多个方法名,给出规范)
让B、C公司分别实现A接口(写一套实现类),看看B、C公司的实现类怎么样,然后哪个实现类好用直接用哪个公司的实现类。 -
案例收获:
1.接口定义的抽象方法不能添加形参
2.传入参数是用在接口的实现类中定义私有的数组,然后定义实现类对象获取数组信息;
3.接口实现类的命名规则是在接口名后加Impl,作为实现类的类名。
public class Test {
public static void main(String[] args) {
// 目标:完成接口的小案例。
// 题目:
/* 1.请设计一个班级学生的信息管理模块:学生的数据有:姓名、性别、成绩
2.功能一:要求打印出全班学生的信息;
功能二:要求打印出全班学生的平均成绩
注意:
第一套方案:能打印出班级全部学生的信息;能打印班级全部学生的平均分
第二套方案:能打印出班级全部学生的信息(包含男女人数)
能打印班级全部学生的平均分(要求是去掉最高分、最低分)
要求:系统可以支持灵活的切换这些实现方案 */
// 1.定义学生类,创建学生对象,封装学生信息,才能交给别人处理
// 2.准备学生数据,自己造一些测试数据
Student[] students = new Student[10];
students[0] = new Student("张三", '男', 100);
students[1] = new Student("李四", '男', 80);
students[2] = new Student("王五", '男', 80);
students[3] = new Student("赵六", '女', 30);
students[4] = new Student("孙七", '女', 90);
students[5] = new Student("钱八", '女', 80);
students[6] = new Student("钱九", '女', 77);
students[7] = new Student("钱十", '女', 90);
students[8] = new Student("钱十一", '女', 20);
students[9] = new Student("钱十二", '女', 100);
// 3.提供两套业务实现方案,支持灵活切换(解耦合):面向接口编程。
// -- 定义一个接口(规范思想):必须完成打印全班学生信息,打印平均分;
// -- 定义第一套实现类,实现接口:实现打印学生信息,实现打印平均分;
// -- 定义第二套实现类,实现接口:实现打印学生信息(男女人数),实现打印平均分(去掉最高分和最低分);
ClassDateInter cdi = new ClassDataInterImpl2(students);
cdi.printAllStudentsInfos();
cdi.printAverageScore();
}
}
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;
}
}
public interface ClassDateInter {
//功能一:要求打印出全班学生的信息
void printAllStudentsInfos();
//功能二:要求打印出全班学生的平均成绩
void printAverageScore();
}
public class ClassDataInterImpl1 implements ClassDateInter {
private Student [] students; // 用来记住送来的全班学生信息数组。
// 用有参构造器来接数据
public ClassDataInterImpl1(Student[] students) {
this.students = students;
}
@Override
public void printAllStudentsInfos() {
for(int i=0; i<students.length; i++){
Student s = students[i];
System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());
}
}
@Override
public void printAverageScore() {
double sum = 0;
for (int i = 0; i < students.length; i++) {
Student s = students[i];
sum += s.getScore();
}
System.out.println("全班同学平均成绩是:" + sum / students.length);
}
}
public class ClassDataInterImpl2 implements ClassDateInter {
private Student [] students; // 用来记住送来的全班学生信息数组。
// 用有参构造器来接数据
public ClassDataInterImpl2(Student[] students) {
this.students = students;
}
@Override
public void printAllStudentsInfos() {
System.out.println("学生信息如下:");
int menNum = 0;
for(int i=0; i<students.length; i++){
Student s = students[i];
if(s.getSex() == '男'){
menNum++;
}
System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());
}
System.out.println("男同学人数是:" + menNum + " ,女同学的人数是:" + (students.length - menNum));
}
@Override
public void printAverageScore() {
System.out.println("平局分如下:");
Student s1 = students[0];
double sum = s1.getScore();
double max = s1.getScore();
double min = s1.getScore();
for (int i = 1; i < students.length; i++) {
Student s = students[i];
sum += s.getScore();
if(s.getScore() > max){
max = s.getScore();
}
if(s.getScore() < min){
min = s.getScore();
}
}
System.out.println("去掉一个最高分:" + max + ",去掉一个最低分:" + min + "\n全班同学平均成绩是:" + (sum - max - min)/ (students.length - 2));
}
}
-
JDK8开始,接口新增了三种形式的方法:默认方法,私有方法,类方法(静态方法)
① 默认方法(普通实例方法):必须加default修饰
默认会用public修饰
如何调用? 使用接口的实现类的对象来调用。
② 私有方法(JDK9开始才支持的)
私有的实例方法。
如何调用? 只能在接口中,使用接口中的其他实例方法来调用
③ 静态方法
默认会用public修饰
如何调用? 只能使用当前接口名来调用。
不允许用实现类来调用当前接口的静态方法。 -
java的接口为什么新增三种形式的方法?
我们自己写的可能性几乎为0,sun公司自己用的比较多。
增强了接口的能力,更便于项目的扩展和维护 -
其实这个在项目中基本用不到,现在三层架构,直接controller自动注入接口的bean,然后在impl实现接口的功能,不会用到这些新增的方法。——可以跳过了
public class Test {
public static void main(String[] args) {
// 目标:搞清楚接口新增的三种方式,并理解其好处。
AImpl a = new AImpl();
a.go(); // ===go方法执行了===
A.show(); // ===show方法执行了===
}
}
class AImpl implements A {
}
public interface A {
// 1、默认方法(普通实例方法):必须加default修饰
// 默认会用public修饰
// 如何调用? 使用接口的实现类的对象来调用。
default void go() {
System.out.println("===go方法执行了===");
run(); // ===go2方法执行了===
}
// 2、私有方法(JDK9开始才支持的)
// 私有的实例方法。
// 如何调用? 只能在接口中,使用接口中的其他实例方法来调用
private void run() {
System.out.println("===go2方法执行了===");
}
// 3、静态方法
// 默认会用public修饰
// 如何调用? 只能使用当前接口名来调用。
// 不允许用实现类来调用当前接口的静态方法。
static void show(){
System.out.println("===show方法执行了===");
}
}
-
接口的注意事项:
1、接口与接口可以多继承:一个接口可以同时继承多个接口;
2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现;(了解)
3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的默认方法,实现类会优先用父类的;
怎么在实现类对象中调用接口的同名方法呢?
只能中转:在实现类写一个方法,用接口名.super.方法名();调用。// 指定调接口爸爸的方法
4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。 -
抽象类和接口的区别对比:
相同点:
1、都是抽象形式,都可以有抽象方法,都不能创建对象
2、都是派生子类形式:抽象类是被子类来使用,几口是被实现类实现。
3、一个类继承抽象类或者实现接口,都必须重写完他们的全部抽象方法,否则自己要成为抽象类
4、都能支持多态,都能够实现解耦合
不同点:
1、抽象类中可以定义类的全部普通成员,接口只能定义常量、抽象方法(JDK8新增的三种方法)
2、抽象类只能被类单继承,接口可以被类多实现
3、一个类继承抽象类就不能再继承其他类,一个类实现了接口(还可以继承其他类,或者实现其他接口)
4、抽象类体现模板思想,更利于做父类,实现代码的复用性;
5、接口更适合做功能的解耦合,解耦合性更强更灵活。 -
最佳实践:
集合都是功能——都是用接口做的解耦合
IO流是抽象类做父类
public class Test {
public static void main(String[] args) {
// 目标:理解接口的几点注意事项
Dog d = new Dog();
d.show();
Dog2 d2 = new Dog2();
d2.show();
}
}
// 1、接口与接口可以多继承:一个接口可以同时继承多个接口;
interface A {
void show1();
}
interface B {
void show2();
}
interface C extends A,B {
void show3();
}
class D implements C {
@Override
public void show1() {
}
@Override
public void show2() {
}
@Override
public void show3() {
}
}
// 2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现;
interface A1 {
void show();
}
interface B1 {
// String show(); // B1用这个,那么D1实现A1和B1的方法会因签名冲突而报错
void show();
}
interface C1 extends A1,B1 {
}
class D1 implements C1 {
@Override
public void show() {
}
}
//3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的默认方法,实现类会优先用父类的;
interface A2{
default void show(){
System.out.println("接口中的 A2 show 方法");
}
}
class Animal{
public void show(){
System.out.println("父类中的 show 方法");
}
}
class Dog extends Animal implements A2 {
public void go(){
super.show(); // 还是找父类的show方法
A2.super.show(); // 找A2接口的show方法
}
}
//4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
interface A3{
default void show(){
System.out.println("接口中的 A2 show 方法");
}
}
interface B3{
default void show(){
System.out.println("接口中的 B2 show 方法");
}
}
class Dog2 implements A3,B3 {
@Override
public void show(){
System.out.println("重写接口中的 show 方法");
A3.super.show(); // 中转
B3.super.show();
}
}
6.其他
-
常量:
使用了static、final修饰的成员变量就称为常量
public static final String SCHOO_NAME = "黑马";
作用:常用于记录系统的配置信息 -
使用常量记录系统配置信息的优势、执行原理:
代码可读性好,可维护性也更好;
程序编译后,常量会被宏替换(就是全部替换):出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和字面量的性能是一样的。 -
设计模式:
设计模式有20多种,对应20多种软件开发过程中会遇到的问题(总体来说有23种) -
关于设计模式,主要学什么?
1、解决什么问题?
2、怎么写? -
枚举类:
枚举类都是最终类,不可以被继承,所有的枚举类都是继承java.lang.Enum类的;
枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象;
所以枚举类是多例模式;
编译器为枚举对象新增了几个方法:枚举类对象名.ordinal():拿到枚举类对象的索引(指代第几个枚举类对象,从0开始) -
比较强大的反编译工具:Xjad
或者用命令行进行反编译:
D:\Desktop>javap .\A.class
Compiled from "A.java"
public final class sun.superstring.enumdemo.A extends java.lang.Enum<sun.superstring.enumdemo.A> {
public static final sun.superstring.enumdemo.A X;
public static final sun.superstring.enumdemo.A Y;
public static final sun.superstring.enumdemo.A Z;
public static sun.superstring.enumdemo.A[] values();
public static sun.superstring.enumdemo.A valueOf(java.lang.String);
static {};
}
D:\Desktop>
-
mybatis把JDBC封装成一个框架
JDBC(Java database Connectivity,Java数据库连接)是Java编程语言中用于执行SQL语句的一套API。它为Java应用程序提供了与各种关系型数据库进行交互的标准方法,使得开发者可以使用统一的接口来访问不同的数据库系统,而无需关心底层数据库的具体实现细节。 -
java的接口为什么新增三种形式的方法?
我们自己写的可能性几乎为0,sun公司自己用的比较多。
增强了接口的能力,更便于项目的扩展和维护
其实这个在项目中基本用不到,现在三层架构,直接controller自动注入接口的bean,然后在impl实现接口的功能,不会用到这些新增的方法。——可以跳过了
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~