final修饰符
1. 使用final修饰类、方法和属性
-
final修饰符可用来修饰类或类中的成员
-
被final修饰的类成为最终类,不能再被子类所继承
public final class FinalClass { … }
-
面试题:为什么String是final的?
-
final修饰的类称为终类,不可以被继承
-
如果String可以被继承,那么开发者可以开发自己的String的子类,并且可以重写String中的各类方法,在后续的程序中,可以直接传入自己开发的String的子类对象,执行的是开发者重写的String方法的逻辑
-
像String这样在整个Java中被广泛应用的类,其方法逻辑如果被修改,会对程序的稳定性造成较大的隐患,因此,设计者将String设计为final的,不允许被继承
-
被final修饰的方法成为最终方法,不能再被子类的方法所覆盖。例如:
public class FinalMethodClass {
public final void method() { … }
}
- 被final修饰的属性称为常量,其值一旦赋予,便不能再被修改。例如:
public class FinalFieldClass {
public final int COUNTER = 100; //常量的声明通常使用大写
}
2. 静态常量
- 大多数情况下,static与final一起使用以修饰一个公用常量。例如:
public static final int MAX_NUM = 65535;
public static final String COMPANY_NAME="小米";
-
这种常量可以在程序的任何位置被直接访问,且值不会被篡改
-
静态常量的命名规则:
- 所有字母都大写
- 单词与单词之间,使用下划线分割
- 不要怕名字太长,要把这个变量的作用说清楚
3. 空final变量
-
在声明常量时,可以暂时不为其指定初始值,称为空final变量
-
空final变量的初始值必须在构造器中指定。例如:
public class FinalFieldClass {
public final int counter;
public FinalFieldClass(int n) {
counter = n;
…
}
}
- 空final变量不能用static来修饰。
抽象类(abstract class)
-
抽象类与具体类
- 具体类 — 对现实世界一种实体的抽象定义 - 比如猫
- 抽象类 — 对现实世界一种类型的多种实体的统一抽象定义 - 比如宠物
-
例如:
- Pet类应定义为抽象类
- Bird类应定义为具体类
-
abstract关键字
- 可用于修饰类和方法
- 修饰类时,该类是抽象类
- 修饰方法时,该方法是抽象方法,没有方法体
-
抽象类中可声明抽象方法
-
另一种抽象类的定义方式:包含抽象方法的类就是抽象类
-
示例:
public abstract class Pet {
public abstract void speak();
……
}
-
抽象类中可以包含的成员包括:
- 属性
- 构造器
- 具体方法
- 抽象方法
public abstract class Pet {
private String name;
private int age;
private int weight;
public Pet(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void speak();
public abstract void eat();
}
-
具体类继承抽象类:
- 抽象类不能被实例化
- 抽象类的抽象方法没有被实现
- 实例与现实世界中一个具体的事物相对应,抽象类没有直接对应的具体事物
- 子类(具体类)可继承抽象父类 ,实现抽象方法
- 具体子类适用抽象父类的多态
- 抽象类不能被实例化
public class Bird extends Pet {
private String type;
public Bird(String name, int age, int weight, String type) {
super(name, age, weight);
this.type = type;
}
public String getType() {
return type;
}
public void speak() {
System.out.println("布谷布谷...");
}
public void eat() {
System.out.println("Eating...");
}
}
public class TestAbstract {
public static void main(String args[]) {
Pet pet = new Bird("小布", 1, 100, "布谷鸟");
pet.speak();
}
}
-
常见问题:
- 抽象类里一定含有抽象方法?不一定
- 含有抽象方法的类一定是抽象类?一定是
- 抽象类里可以定义属性和方法?可以
- 抽象类里可以定义构造方法?可以
- 抽象类可以创建对象?不可以
- 抽象方法可以被private/final/static分别修饰?都不行,抽象方法只有被子类重写,才能够实际执行,被private/final/static修饰的方法都不能被重写,因此不能用它们修饰抽象方法
- 抽象类可以被final修饰?不可以,抽象类不能被实例化,需要被继承才能实例化。final修饰会限定抽象类不能被继承
- 抽象类是否可以继承抽象类?
- 抽象类可以继承抽象类,可以实现其中定义的一部分抽象方法,留下另一部分抽象方法由具体子类实现
中场总结:
-
引用类型转换
- a instanceof Person:判断a引用的是否是Person或其子类的对象
- Student s=(Student)p;
-
对象的关联
- 一个类中的某个属性的类型是另一个类
- 当实例化一个对象时,它的一个属性值是另一个对象的地址,也就是A对象通过属性关联了B对象
-
static(静态的)
- static修饰属性:静态变量,可以通过类名.变量名直接访问,所有地方访问的是同一个静态变量
- static修饰方法:静态方法,可以通过类名.方法名直接调用,常用于各种工具类的方法
- static修饰代码块:静态代码块(静态初始化器),用于初始化本类的静态成员(变量)
- 在类加载时执行1次
- 题目:
- 静态代码块和构造代码块的区别
- 静态代码块使用static修饰的代码块,
static{}
,又称为静态初始化器,用于初始化本类的静态成员,在类加载时执行一次 - 构造代码块是声明在类中的
{}
,用于初始化本类的实例成员,在每次创建该类的实例时执行1次
- 静态代码块使用static修饰的代码块,
- 构造代码块和构造器什么区别
- 构造代码块先于构造器执行
- 构造器可以分为无参的和带参的,带参构造器可以接收用户创建对象时传入的参数,为对应的属性赋值
- 构造代码块不能接收参数,可以将所有构造器都要执行的逻辑放入构造代码块中
- 构造代码块{ 逻辑1,逻辑2 }
- 构造器1{逻辑3}
- 构造器2{逻辑4}
- 构造器3{逻辑5}
- 静态代码块和构造代码块的区别
-
final
- 修饰类-> 终类 -> 不能被继承
- 修饰方法 -> 最终方法 -> 不能被重写
- 修饰属性 -> 常量 -> 赋值后值不能被修改
- 一般和static搭配,成为静态常量
- 可以在程序任意位置被访问,且不会被修改
- 命名规范:所有单词都大写,单词与单词之间使用下划线分割
- 一般和static搭配,成为静态常量
-
抽象类
- 含有抽象方法的类是抽象类/使用 abstract修饰的类就是抽象类
- 抽象方法:
- 使用 abstract修饰的方法
- 没有方法体
- 不能被private/static/final修饰
- 抽象类不能被实例化
- 一个具体类继承了抽象类,必须实现抽象类中定义的所有抽象方法
- 抽象类只能实例化它的具体子类(多态引用)
接口
1. 接口的概念
-
接口的用途是用来定义现实世界不同类型事物的共同行为特征。
-
例如:
- 飞行特征(包括起飞,飞行、着陆等行为)是鸟和飞机所共有的。
- 鸟和飞机可定义为具体类
- 它们的共同行为特征可被定义在接口中
-
接口中所有方法均为抽象方法
-
示例:
public interface Flyer {
public void takeoff();
…… }}
2. 接口的成员
-
接口可以包含以下成员:
-
属性
- 接口中的所有属性均被视静态常量。例如,下面几种方式的声明是等效的:
-
int num = 10;public int num = 10;
public static final int num = 10;
-
抽象方法
- 接口中所有方法均为抽象方法。例如,下面两种方式的声明是等效的:
public abstract void takeoff();
public void takeoff();
-
具体方法
- JDK1.8中为了加强接口的能力,使得接口可以存在具体的方法,前提是方法需要被default或static关键字所修饰
public interface Flyer {
public abstract void takeoff();
public void land();
public void fly();
}
3. 具体类实现接口
- 接口不能被实例化
- 具体类(子类)可以实现接口(父类) ,并实现接口中的抽象方法
- 具体类适用父接口的多态
- Java语言允许一个类同时实现多个接口
- 子类必须提供所有父接口中所有方法的实现
public class Bird implements Flyer {
private String type;
public Bird() {}
public String getType() {
return type;
}
public void takeoff() {
System.out.println("I'm taking off...");
}
public void land() {
System.out.println("I'm landing...");
}
public void fly() {
System.out.println("I'm flying...");
}
}
public class TestInterface {
public static void main(String args[]) {
Bird bird = new Bird();
bird.takeoff();
Flyer flyer = new Bird();
flyer.takeoff();
}
}
4. 具体类-抽象类-接口
- 很多情况下:
- 抽象类实现接口,并可实现接口中的部分方法
- 具体类继承抽象类,并实现所有抽象方法
-
同时继承父类并实现接口
- 一个类可以同时继承父类并实现接口
5. 抽象类和接口的区别
- 抽象类是类,接口不是类
- 一个类继承抽象类使用extends关键字,实现接口使用implements关键字
- 一个接口可以使用extends关键字继承另一个接口
- 一个类仅能继承一个抽象类,但是可以实现多个接口
- 抽象类的目的是延展类继承结构,接口是为了注入更多的特性
6. 接口的有点
- 模板
- 约束
内部类
1. 在类中定义类
方法内部类
- 方法内定义类
- 类里可以定义所有的非静态信息以及静态常量
- 可以进行正常的继承和实现
- 不能被public/默认来修饰但是可以被final/abstract来修饰
- 可以拿到外部类所有信息
- 只能拿到本方法的常量信息
- 创建对象只能在本方法里创建对象
public class InnerDemo1{
public static void main(String[] args){
//外部类对象调用m方法执行里面创建内部类语句
newOuter1().m();
}
}
//外部类
class Outer1{
//属性
intk=10;
//方法
public void m(){
intj=1;//再jdk1.8之前需要手动写上final才是常量
//在jdk1.8及其以后版本不写fianl底层也会添加final
//j=2;//没有检测到内部类之前还是正常变量操作,但是一旦检测
//到方法内部类需要加上final
//方法内部类
//可以定义所有的非静态信息以及静态常量
//可以进行正常的继承和实现
//不能被public以及默认修饰类
//可以被final/abstract来修饰
//内部类可以获取外部类所有的信息
//方法内部类只能获取本方法里的常量信息
class Inner1 extends Object implements Cloneable{
static final int i=1;
public void n(){
System.out.println(k);
System.out.println(j);
}
}
//创建方法内部类对象
Inner1 in=new Inner1();
in.n();
}
}
成员内部类
-
类内方法外定义类
-
定义所有的非静态信息以及静态常量
-
正常继承和实现
-
被访问权限修饰符以及final和abstract来修饰
-
拿到外部类的所有信息
Outer1.Inner1 inner1=new Outer1().new Inner1();
public class InnerDemo1{
public static void main(String[] args){
// 创建成员内部类对象
// 声明---内部类
Outer1.Inner1 inner1=new Outer1().newInner1();
}
}
//外部类
class Outer1{
//属性(成员变量)
inti=1;
//创建成员内部类对象---外部类的属性
//Inner1in1=newInner1();
//成员内部类
//可以定义所有的非静态信息以及静态常量
//可以进行正常的继承和实现
//支持所有的访问权限修饰符来修饰以及final和abstract
//可以拿到外部类所有的信息
class Inner1 extends Object implements Cloneable{
static final int j=10;
public void n(){
System.out.println(i);
m();
}
}
//方法(成员方法)
public void m(){}
}
静态内部类
-
类内方法外定义被static修饰的类
-
定义所有信息
-
正常继承和实现
-
被访问权限修饰符以及final和abstract来修饰
-
拿到外部类的静态信息
Outer2.Innner2 innner2=new Outer2.Innner2();
public class InnerDemo2{
public static void main(String[] args){
//创建静态内部类对象
Outer2.Innner2innner2=newOuter2.Innner2();
}
}
//外部类
class Outer2{
//属性
static int i=1;
//静态内部类
//定义所有的信息
//进行继承和实现
//可以被访问权限修饰符以及final和abstract
//只能获取外部类的所有静态信息
static class Innner2 extends Object implements Cloneable{
static final int j=10;
public void n(){
System.out.println(i);
}
}
//方法
public void m(){}
}
匿名内部类
-
用于继承类/实现接口来重写(抽象)方法成创建对象使用
-
只要类可以有被继承/接口可以被实现都可以有匿名内部类形式
-
匿名内部类主要适用的场景就是传参,只能使用一次
public class InnerDemo3{
public static void main(String[] args){
//{}区域就是匿名内部类
//匿名内部类默认继承抽象类重写抽象方法并且创建匿名内部类对象
//当类可以被继承时/接口可以被实现,就可以拥有匿名内部类形式
C c=new C(){
@Override
public void m(){
}
};
//普通类也具有匿名内部类形式
B b=new B(){};
//接口也具有匿名内部类形式
A a=new A(){};
//调用方法传入接口实现类对象
//匿名内部类对象传入即可
//当做参数使用
//匿名内部类使用方便但是只能使用一次
m(newA(){});
//创建抽象类子类对象
Dd=newD();
}
//参数类型是接口只能传入接口的实现类对象
public static void m(A a){}
}
interface A{}
class B{}
abstract class C{
public abstract void m();
}
class D extends C{
@Override
public void m(){}
}
2. 在接口中定义类
- 接口里定义类,内部类接口都是默认被static修饰
public class InnerDemo4{
public static void main(String[] args){
System.out.println(Outer4.Inner4.i);
System.out.println(Outer4.Inner5.j);
System.out.println(Outer4.Inner4.Inner6.k);
}
}
interface Outer4{
//内部接口默认被static修饰
static interface Inner5{
intj=2;
}
//内部类默认被static修饰
static class Inner4{
static int i=1;
//内部接口默认被static修饰
static interface Inner6{
int k=10;
}
}
}