参考文章:
- 接口相关:https://blog.csdn.net/qq_19782019/article/details/80259836
- 默认方法:https://www.cnblogs.com/sidesky/p/9287710.html
1.抽象类
-
定义:作为一个不能实例化,只能作为父类被继承的类,抽象类其实就是从多个具体的类中抽象出来的,具有更高层次的抽象。
-
特点:
- 抽象的方法只作声明,不包含实现,可以看成没有实现的虚函数。
- 抽象类不可以被实例化
- 抽象类不是必须需要抽象方法,也就是你可以在抽象类中写任何普通的方法,但是一旦你的类中包含了抽象方法,则这个类必须是抽象类,否则会报错。
- 具体派生类必须重写基类的抽象方法
- 抽象的派生类可以不覆盖抽象的父类的抽象方法,如果不覆盖,其具体的派生类也必须覆盖这些方法。
2. 接口
- 定义:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解,JDK1.8后出现了默认方法,便于解耦)
- 特点:
- 不可实例化
- 一个类可以实现多个接口
- 一个接口可以继承多个接口
- 接口里的属性字段是默认public static final修饰的,接口的方法都是==public abstract ==修饰的。
- 注意点:
- 接口的标识用法: 虽然接口内部定义了一些抽象方法,但是并不是所有的接口内部都必须要有方法,比如Seriallizable接口
3.抽象类与接口的区别
- 接口的特点:
-
接口只能包含方法声明
-
接口的成员包含方法,属性,索引器,事件
-
接口中不能包含变量,字段,构造和构析函数,静态方法。
-
jdk8后可以添加默认方法
默认方法相关
- 作用:为了使接口和实现类的耦合度降低
- 继承:
- 接口默认方法的继承分三种情况(分别对应上面的 InterfaceB 接口、InterfaceC 接口和 InterfaceD 接口):
- 不覆写默认方法,直接从父接口中获取方法的默认实现。
- 覆写默认方法,这跟类与类之间的覆写规则相类似。
- 覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
https://blog.csdn.net/u010003835/article/details/76850242
- .默认方法例子
interface InterfaceA {
default void foo() {
System.out.println("InterfaceA foo");
}
}
class ClassA implements InterfaceA {
}
public class Test {
public static void main(String[] args) {
new ClassA().foo(); // 打印:“InterfaceA foo”
}
}
- 抽象和接口区别
-
抽象类可以拥有构造方法
-
抽象类可以有普通方法成员
-
抽象类可以包含静态方法
-
一个类可以实现多个接口,只能继承一个类
-
如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现。在抽象类的子类中实现接口中方法
- 相同点
- 都可以被继承(类继承类,接口可以继承接口)
- 都不可以被实例化
- 都可以包含方法声明
- 派生类必须实现未实现的方法
4. 各种修饰符
4.1final 修饰符
- 修饰类:不能被继承
- 修饰方法:不能被重写
- 修饰变量:初始化赋值,则不可以被改变。
4.2static修饰符
1.与实例对象的区别
-
在程序运行时:实例对象属于某个对象的属性,必须创建了实例对象,其中的实例变量才会分配空间,才能使用这个实例变量,静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只有程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,然后直接使用。
-
内存的区别:
静态变量位于方法区,被类的所有实例共享,静态变量可以直接通过类名进行访问,其生命周期取决于类的生命周期。
实例变量取决于类的实例,每创建一个实例,jvm就会为实例变量分配一次内存,实例变量位于堆区中,其生命周期取决于实例的生命周期。 -
方法声明为static时,具体有如下限制:
1):只能调用static方法和使用static属性
2):不能使用关键字this或super。
3):static代码块将被只执行一次。
首先Java的static类只能是静态内部类。如果在外部类声明为static,程序会编译通不过。
静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
其次,主要了解下static内部类与普通内部类的区别是什么,以及static内部类的作用是什么,详见下表:
4.3. 访问修饰符
https://www.nowcoder.com/test/question/done?tid=27458807&qid=56132#summary(很全面)
- public:共有访问。对所有的类都可见。
- protected:保护型访问。对同一个包可见,对不同的包的子类可见。
- default:默认访问权限。只对同一个包可见,注意对不同的包的子类不可见。
- private:私有访问。只对同一个类可见,其余都不见。
5.内部类
作用:
- 内部类提供了更加好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类的成员,同一个类可以相互访问,但外部类不能访问内部类的实现细节,例如内部类的成员变量
- 匿名内部类适用于创建那些仅需要一次适用的类。
6.匿名内部类
参考文章:https://blog.csdn.net/hellocsz/article/details/81974251
举三个例子,分别说明没有使用匿名内部类,和使用匿名内部类实现接口和抽象类的情况
//定义一个抽象类
abstract class Person {
public abstract void eat();
}
//定义一个接口类
interface Person1 {
public void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
/**
一.不使用匿名内部类
必须继承类或实现接口
*/
Person p = new Child();
p.eat();
/**
二,实现接口
*/
Person1 p1 = new Person1() {
public void eat() {
System.out.println("eat something");
}
};
p1.eat();
/**
三,实现类
*/
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
7. 父子类构造函数的调用顺序
首先是父类的静态变量,然后是子类的静态变量,然后是父类非静态变量,父类构造函数,子类非静态变量,子类构造函数
package com.web;
/**
* 功能说明:TODO
*
* @date 2019年5月8日
* @author 马琳-君子自强,脚踏实地积累
* @email 1217575485@qq.com
*
*/
public class Abc {
public static void main(String[] args) {
SubClass subClass = new SubClass();
System.out.println(subClass.field);
}
}
class ParentClass {
static String a="静态父类成员";
static void get(){
System.out.println("加载父类静态代码块");
}
String field = "父类变量";
public ParentClass() {
System.out.println("加载父类构造方法");
}
}
class SubClass extends ParentClass {
static String a="静态子类成员";
static void get(){
System.out.println("加载父类静态代码块");
}
String field = "子类变量";
public SubClass(String field) {
System.out.println("加载子类构造方法2");
}
public SubClass( ) {
System.out.println("加载子类构造方法");
}
}