static、代码块、懒汉单例、继承、权限修饰符
static静态关键字
静态成员变量会在堆内存中存储数据(单独存储,并不会存储在new出来的对象中)
static是什么,static修饰成员变量的用法
-
static是什么
- static是静态的意思,可以用来修饰成员变量、成员方法。
- static修饰成员变量之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)。
- static修饰后的成员变量,可以被类的所有对象共享(访问、修改)。
- static修饰的成员变量是什么? 有什么特点?
- 静态成员变量(有static修饰,属于类、加载一次,内存中只有一份),访问格式
类名.静态成员变量(推荐)
对象.静态成员变量(不推荐)。
2. 实例成员变量(无static修饰,属于对象),访问格式:
对象.实例成员变量
3. 两种成员变量各自在什么情况下定义?+ 静态成员变量:表示在线人数等需要被类的所有对象共享的信息时。+ 实例成员变量:属于每个对象,且每个对象的该信息不同时(如:name,age,money…)
- 执行原理
static修饰成员方法的用法
- 成员方法的分类和访问分别是什么样的?
-
静态成员方法(有static修饰,属于类和对象共享)访问格式:
-
类名.静态成员方法。
-
对象.静态成员方法。(不推荐)
-
-
-
实例成员方法(无static修饰,属于对象)的访问格式:
- 对象.实例成员方法。
-
每种成员方法的使用场景是怎么样的?
-
表示对象自己的行为的,且方法中需要直接访问实例成员,则该方法必须申明成实例方法。
- 如果该方法是以执行一个通用功能为目的,或者需要方便访问,则可以申明成静态方法
-
static访问注意事项:
- 静态方法只能访问静态的成员,不可以直接访问实例成员。
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中是不可以出现this关键字的。
static应用知识:
工具类
-
工具类是什么,有什么好处?
- 内部都是一些静态方法,每个方法完成一个功能
- 一次编写,处处可用,提高代码的重用性。
-
工具类有什么要求?
-
建议工具类的构造器私有化处理。
-
工具类不需要创建对象。
-
代码块
代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)。
- 代码块分为两种:
- 静态代码块:
- 格式:static { }
- 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
- 作用:完成类的初始化,例如:对类变量的初始化赋值。
- 静态代码块:
- 实例代码块(了解):
- 格式:{ }
- 特点:每次创建对象时,执行实例代码块,并在构造器前执行。
- 作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值。
总结
static | 修饰变量 | 修饰方法 | 修饰代码块 |
---|---|---|---|
隶属 | 类(静态变量) | 类(静态方法) | 类(静态代码块) |
推荐访问方式 | 类名.变量名 | 类名点.方法名() | 类加载时自动执行 |
经典使用场景 | 多对象共享变量 | 工具类方法 | 为类变量初始化赋值 |
饿汉单例的实现步骤?
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象
懒汉单例设计模式
面向对象三大特征之二:继承
继承概述、使用继承的好处
- 什么是继承? 继承的好处是啥?
- 继承就是java允许我们用extends关键字,让一个类和另一个类建立起一种父子关系。
- 提高代码复用性,减少代码冗余,增强类的功能扩展性。
- 继承的格式
- 子类 extends 父类
- 继承后子类的特点?
- 子类 继承父类,子类可以得到父类的属性和行为,子类可以使用。
- Java中子类更强大
继承满足的设计规范
- 子类们相同特征(共性属性,共性方法)放在父类中定义。
- 子类独有的的属性和行为应该定义在子类自己里面。
继承的特点
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
- Java是单继承模式:一个类只能继承一个直接父类。
- Java不支持多继承、但是支持多层继承。
- Java中所有的类都是Object类的子类。
继承的执行原理
继承相关的注意事项
权限修饰符
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问的范围。
- 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
权限修饰符的分类和具体作用范围:
权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected - > public )
修饰符 | 本类里 | 同一个包中的类 | 子孙类 | 任意类 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
单继承
Java是单继承的,Java中的类不支持多继承,但是支持多层继承。
- Object类
- Object类是java所有类的祖宗类。我们写的任何一个类,其实都是Object的子类或子孙类。
继承后:方法重写
-
方法重写是什么样的?
- 子类写一个与父类申明一样的方法覆盖父类的方法。
-
方法重写建议加上哪个注解,有什么好处?
- @Override注解可以校验重写是否正确,同时可读性好。
-
重写方法有哪些基本要求?
- 重写方法的名称和形参列表应该与被重写方法一致。
- 私有方法不能被重写。
- 子类重写父类方法时,访问权限必须大于或者等于父类被重写的方法的权限。
-
子类继承父类后构造器的特点是什么样的?
- 子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。
-
super调用父类构造器的作用是什么?
- 通过调用父类有参数构造器来初始化继承自父类的数据
继承后:成员变量、成员方法的访问特点
-
在子类方法中访问成员(成员变量、成员方法)满足:
-
就近原则,子类没有找子类、子类没有找父类、父类没有就报错!
-
如果子父类中出现了重名的成员,此时如果一定要在子类中使用父类的怎么办?
格式:super.父类成员变量/父类成员方法
- 如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?
- 可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
super.父类成员变量/父类成员方法
- 可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
子类构造器的特点
-
子类构造器的特点
- 子类的全部构造器,都会先调用父类的构造器,再执行自己。
-
子类构造器是如何实现调用父类构造器的
- 默认情况下,子类全部构造器的第一行代码都是 super() (写不写都有) ,它会调用父类的无参数构造器。
- 如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(….),指定去调用父类的有参数构造器。
-
子类构造器调用父类构造器的应用场景
- 我们经常在子类构造器中调用父类的有参构造器给父类传递参数来完成父类对象的初始化
-
子类构造器调用父类构造器的内存图
this和super详情
this:代表本类对象的引用;super:代表父类存储空间的标识。
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构器 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造器 |
- this(…)和super(…)使用注意点:
- 子类通过 this (…)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
- 注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。
多态、final、抽象类、接口、常量、枚举
多态
什么是多态,常见形式,访问特点, 优势?
- 多态是在继承或者实现情况下的一种现象,表现为:对象多态、行为多态。
多态的常见形式
父类类型 对象名称 = new 子类构造器;
多态中成员访问特点
-
方法调用:编译看左边,运行看右边。
-
变量调用:编译看左边,运行也看左边。(注意)
-
class Father {void parentMethod() {System.out.println("父类的 parentMethod() 方法被调用");}int parentVariable = 10; }class Son extends Father {@Overridevoid parentMethod() {System.out.println("子类的 parentMethod() 方法被调用");}int sonVariable = 20; }public class PolymorphismExample {public static void main(String[] args) {// 创建父类的实例Father father = new Son();// 方法调用:编译看左边,运行看右边father.parentMethod(); // 变量调用:编译看左边,运行也看左边int variable = father.parentVariable; } }
多态的前提
- 有继承/实现关系;
- 有父类引用指向子类对象;
- 有方法重写(多态侧重行为多态)
多态的一个注意事项
- 多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。
优势
-
在多态形式下,等号左右两边对象松耦合,便于扩展和维护。
-
Animal a = new Dog();
a.run(); // 后续业务行为随对象而变,后续代码无需修改
-
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
多态转换问题
多态下会产生的一个问题:
- 多态下不能使用子类的独有功能
自动类型转换(从子到父)
从子到父: Animal c=new Cat();
强制类型转换(从父到子)
-
从父到子: (必须进行强制类型转换,否则报错):
子类 对象变量=(子类)父类类型的变量
-
作用: 可以解决多态下的劣势, 可以实现调用子类独有的功能
-
注意: 有继承/实现关系的类就可以在编译阶段进行强制类型装换; 但是, 如果转换后的类型和对象真实的类型不是同一类型,那么在运行代码时,就会出现 ClassCastException类型转换异常
Animal c=new Cat();
Dog d=new (Dog)c;//出现异常ClassCastException
-
Java建议强制转换前使用instanceif判断当前对象的真实类型,再进行强制转换
变量名instanceof真实类型
判断关键字左边的变量指向的对象的真实类型, 是否是右边的类型或者是其子类类型, 是则返回true, 反之.
总结
- 1、使用多态有什么好处?存在什么问题?
- 可以解耦合,扩展性更强;使用父类类型的变量作为方法的形参时,可以接收一切子类对象
- 多态下不能直接调用子类的独有方法
- 2、类型转换有几种形式?能解决什么问题?
- 自动类型转换、强制类型转换。
- 可以把对象转换成其真正的类型,从而解决了多态下不能调用子类独有方法的问题
- 3、强制类型转换需要注意什么?
- 存在继承/实现时,就可以进行强制类型转换,编译阶段不会报错
- 但是运行时,如果发现对象的真实类型与强转后的类型不同会报错(ClassCastException)
- 4、强制类型转换前?Java建议我们做什么事情?
- 使用instanceof判断当前对象的真实类型:对象 instanceof 类型
final
final的作用: final 关键字是最终的意思,可以修饰(类、方法、变量)
-
修饰类:表明该类是最终类,不能被继承。
-
修饰方法:表明该方法是最终方法,不能被重写。
-
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
-
成员变量: 声明时或者在构造方法结束之前完成赋值
局部变量: 在使用之前完成赋值
-
final修饰变量的注意
- final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
- final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
抽象类
什么是抽象类
- 在Java中abstract是抽象的意思,可以修饰类、成员方法。
- abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
修饰符 abstract class 类名{ 修饰符 abstract 返回值类型 方法名称(形参列表);}
public abstract class Animal{public abstract void run();}
特征和注意事项
特征:
- 类有的成员(成员变量、方法、构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能用abstract修饰变量、代码块、构造器。
- 最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)
注意事项:
- 抽象方法只有方法签名,不能声明方法体。
- 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。
final和abstract是什么关系?
- 互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
Java中protected的用法
在Java中,抽象类和protected修饰符经常一起使用,因为抽象类通常用来定义一些抽象的行为和属性,而这些行为和属性可能需要在子类中进行实现或访问。
当一个成员(字段或方法)在抽象类中被声明为protected时,这意味着这个成员可以被子类访问,但不能被在包外部的类访问。这样可以确保子类可以继承和访问父类中的一些共享的资源,同时防止外部类对这些资源的直接访问。
public abstract class Shape {protected double area;protected abstract void calculateArea();public void displayArea() {System.out.println("Area: " + area);}
}public class Circle extends Shape {private double radius;public Circle(double radius) {this.radius = radius;}@Overrideprotected void calculateArea() {area = Math.PI * radius * radius;}
}public class Main {public static void main(String[] args) {Circle circle = new Circle(5.0);circle.calculateArea();circle.displayArea();}
}
在上面的例子中,Shape类是一个抽象类,其中的area字段被声明为protected,子类Circle可以访问并计算area的值。同时,Shape类中的calculateArea方法也被声明为protected,子类Circle必须实现这个方法来计算具体的面积。最后,在Main类中创建了一个Circle对象并调用了calculateArea和displayArea方法来计算和显示圆的面积。
总的来说,抽象类和protected修饰符的结合使用可以有效地实现类之间的继承和封装。
总结
-
抽象类、抽象方法是什么样的?
- 都是用abstract修饰的;抽象方法只有方法签名,不能写方法体。
- 一个类中定义了抽象方法,这个类必须声明成抽象类。
-
抽象类主要特点
作为父类,用来被继承的。
-
继承抽象类有哪些要注意?
一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
方法模版使用场景
- 当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤
- 定义一个抽象类。
- 定义2个方法,一个是模板方法:把相同代码放里面去,不同代码定义成抽象方法
- 子类继承抽象类,重写抽象方法。
模板方法模式解决了什么问题?
- 提高了代码的复用性
- 模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。
public abstract class Animal {private String name;public abstract void cry();public String getName() {return name;}public void setName(String name) {this.name = name;}}
public class Dog extends Animal {@Overridepublic void cry() {System.out.println(getName() + "汪汪汪的叫~~~");}}
public class Cat extends Animal {@Overridepublic void cry() {System.out.println(getName() + "喵喵喵的叫 ~~~");}}
接口
接口的定义与特点
- 接口的格式如下:
接口用关键字interface来定义public interface 接口名 {成员变量(接口中的成员变量都是常量, 默认是被public static final修饰的)成员方法(接口中的成员方法都是抽象方法, 默认是被public abstract修饰的)注意: 接口中不能有构造方法和代码块}
- JDK8之前接口中只能是抽象方法和常量,没有其他成分了。
特点:
-
接口不能实例化(创建对象)。
-
接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化。
-
一个类可以实现多个接口,实现类实现多个接口,必须重写完全部接口的全部抽象方法,
否则实现类需要定义成抽象类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... { }
小结
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
接口多继承的作用
- 规范合并,整合多个接口为同一个接口,便于子类实现。
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。(解耦合)
补充知识:JDK8开始接口新增的方法
public interface A { /** 1、默认方法(jdk8开始支持):对接口中的方法提供默认实现* 使用default修饰,有方法体,可以但是不强制要求实现类重写, 只能通过实现类的对象调用 */default void test1() { ... }/*** 2、静态方法(jdk8开始支持):方便调用* 使用static修饰,有方法体,只能通过接口名调用* */static void test2() { ... }/*** 3、私有方法(jdk9开始支持):提高代码复用性* 使用private修饰,服务于接口内部,用于抽取相同的功能代码 */private void test3() { ... }
}
总结
JDK8开始后新增了那些方法?
-
默认方法:default修饰,实现类对象调用。
-
静态方法:static修饰,必须用当前接口名调用
-
私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
-
他们都会默认被public修饰。
接口的注意事项
1、接口不能创建对象
2、一个类实现多个接口,多个接口的规范不能冲突
2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。
3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
常量
常量概述和基本作用
- 常量
- 使用了
static final
修饰的成员变量就被称为常量; - 作用:通常用于记录系统的配置信息。
- 使用了
public class Constant { public static final String SCHOOL_NAME = “传智教育”; }
注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。
使用常量记录系统配置信息的优势、执行原理
- 代码可读性更好,可维护性也更好。
- 程序编译后,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
常量做信息标志和分类
案例说明:
- 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。
选择常量做信息标志和分类:
- 代码可读性好,实现了软编码形式。
枚举
枚举的概述
- 枚举是Java中的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类”。
定义枚举类的格式:
修饰符 enum 枚举名称{第一行都是罗列枚举类实例的名称。
}
enum Season{SPRING , SUMMER , AUTUMN , WINTER;}
枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
ing SCHOOL_NAME = “传智教育”; }
注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。
使用常量记录系统配置信息的优势、执行原理
- 代码可读性更好,可维护性也更好。
- 程序编译后,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
常量做信息标志和分类
案例说明:
- 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。
选择常量做信息标志和分类:
- 代码可读性好,实现了软编码形式。
枚举
枚举的概述
- 枚举是Java中的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类”。
定义枚举类的格式:
修饰符 enum 枚举名称{第一行都是罗列枚举类实例的名称。
}
enum Season{SPRING , SUMMER , AUTUMN , WINTER;}
枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。