Java面向对象高级一
- [ 任务列表 ]
- 1.final
- 2.单例类
- 3.枚举类
- 4.抽象类
- 5.接口
- 6.其他
—————————————————————————————————————————————————————————————————————
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@2f4d3709System.out.println(a2); // 地址:sun.superstring.singleinstance.A@2f4d3709System.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@1d81eb93System.out.println(b2); // 地址:sun.superstring.singleinstance.B@1d81eb93System.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); // XA a2 = A.Y;System.out.println(a2.toString()); // YSystem.out.println(a1.name()); // XSystem.out.println(a2.name()); // YSystem.out.println(a1.ordinal()); // 可以拿枚举对象的索引 0System.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()); // 小明 18b.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必须定义成抽象类@Overridepublic 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{@Overridepublic void shout() {System.out.println("猫是喵喵喵~~~");}
}public class Dog extends Animal{@Overridepublic 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{@Overridepublic 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{@Overridepublic 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 {@Overridepublic void study() {System.out.println("C类重写了study方法");}@Overridepublic String go() {return "黑马找磊哥";}@Overridepublic 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;}@Overridepublic void printAllStudentsInfos() {for(int i=0; i<students.length; i++){Student s = students[i];System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());}}@Overridepublic 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;}@Overridepublic 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));}@Overridepublic 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 {@Overridepublic void show1() {}@Overridepublic void show2() {}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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实现接口的功能,不会用到这些新增的方法。——可以跳过了