- 面向对象
- 封装
- 访问修饰符
- 封装的步骤
- 继承
- 继承说明
- super 关键字
- super 和 this 的比较
- 方法重写/覆盖(override)
- 重写和重载比较
- 多态
- 编译类型和运行类型
- 多态细节
- 多态的向上转型
- 多态的向下转型
- 属性
- Java 动态绑定机制
- Object类详解
- == 和 equals 的对比
- toString 方法
- finalize 方法
- 类变量
- 定义类变量
- 如何访问类变量
- 类变量细节
- 类方法细节
- 代码块
- 基本介绍
- 语法
- 代码块细节
- 类的调用顺序
- 创建子类对象时,类的调用顺序
- 单例设计模式
- 单例
- 单例模式实现步骤
- 饿汉式与懒汉式
- final关键字
- 基本介绍
- 什么情况使用final
- final使用细节
- 抽象类
- 抽象类介绍
- 接口
- 注意细节
- 内部类
- 内部类的分类
- 局部内部类
- 匿名内部类
- 成员内部类
- 静态内部类
| 访问级别 | 访问修饰符 | 同类 | 同包 | 子类 | 不同包 |
|---|---|---|---|---|---|
| 公开 | public | √ | √ | √ | √ |
| 受保护 | protected | √ | √ | √ | × |
| 默认 | 无修饰符 | √ | √ | × | × |
| 私有 | private | √ | × | × | × |
- 将属性私有化 【不能直接修改属性】
- 提供一个公共的(public)set 方法,用于对属性判断并赋值
- 提供一个公共的(public)get 方法,用于获取属性的值
- 子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
- 子类必须调用父类的构造器, 完成父类的初始化。
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
- 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表) 。
- super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)。
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。
- Java 所有类都是 Object 类的子类, Object 是所有类的基类。
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)。
- 子类最多只能继承一个父类(指直接继承),即 Java 中是单继承机制。
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系。
-
访问父类的属性,但不能访问父类的private属性
super.属性名;
-
访问父类的方法,不能访问父类的private方法
super.方法名(参数列表)
-
访问父类的构造器
super(参数列表);只能放在构造器的第一句,只能出现一句!
说明:方法是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的方法重写了父类的方法。
方法重写需要满足的条件:
重写和重载比较| 名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
|---|---|---|---|---|---|
| 重载(overload) | 本类 | 必须一样 | 类型,个数或者顺序至少有一个不同 | 无要求 | 无要求 |
| 重写(override) | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
- 一个对象的编译类型和运行类型可以不一致。
- 编译类型在定义对象时就确定了,不能改变。
- 运行类型可以变化。
- 编译类型看定义时 = 的左边,运行类型看 = 的右边。
多态的前提是:两个对象(类)存在继承关系
多态的向上转型- 本质:父类的引用指向子类的对象
- 语法:父类类型 引用名 = new 子类类型()
- 特点:编译类型看左边,运行类型看右边。
- 可以调用父类中的所有成员(需遵守访问权限),不能调用子类中的特有成员。
- 语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
- 属性没有重写之说,属性的值看编译类型,方法的调用看运行类型。
- instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型。
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
System.out.println(a.sum());//30
System.out.println(a.sum1());//20
}
}
class A {//父类
public int i = 10;
//动态绑定机制:看a运行类型调用子类B的getI()方法
public int sum() {//父类 sum()
return getI() + 10;
}
public int sum1() {//父类 sum1()
return i + 10;//属性没有动态绑定机制,直接返回当前类的值
}
public int getI() {//父类 getI
return i;
}
}
class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类 getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}
Object类详解
== 和 equals 的对比
- == :既可以判断基本类型,又可以判断引用类型。
- == :如果判断基本类型,判断的是值是否相等。
- == :如果判断引用类型,判断的是地址是否相等,即判断是不是一个对象。
- equals :是Object类中的方法,只能判断引用类型。
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
-
基本介绍:默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】 子类往往重写 toString 方法,用于返回对象的属性信息。
-
重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式。
-
当直接输出一个对象时,toString 方法会被默认的调用。
比如 System.out.println(monster); 就会默认调用 monster.toString()
- 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作。
- 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
- 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
定义类变量访问修饰符 static 数据类型 变量名;[推荐]
static 访问修饰符 数据类型 变量名;
类名.类变量名 对象名.类变量名
类变量细节- 当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)
- 类变量是该类的所有对象共享的,实例变量是每个对象独享的
- 加上static成为类变量或静态变量,否则成为实例变量/普通变量/非静态变量
- 类变量是在类加载时就初始化了,及时没有创建对象,只要类加载了,就可以使用类变量了
- 类变量的生命周期是随着类的加载开始,随着类的消亡而销毁。
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数
- 类方法可以通过类名调用,也可以通过对象名调用
- 普通方法和对象有关,需要通过对象名调用,不能通过类名调用。
- 类方法中不允许使用和对象有关的关键字,比如this和super。
- 类方法(静态方法)中只能访问静态变量或静态方法。
- 普通成员方法,既可以访问非静态成员,也可以访问静态成员。
代码块又称初始代码块,属于类中的成员,类似与方法,通过{}包围起来。
代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过类加载或类显式调用,而是随着类加载时,或创建对象时隐式调用。
[修饰符]{}
修饰符只能是 static,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。
-
static代码块(静态代码块),作用就是对类进行初始化,她随着类的加载而执行,只会执行一次。普通代码块每创建一个对象,就执行一次。
-
类什么时候被加载
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
-
普通代码块,在创建对象实例时,会被隐式的调用。
每创建一次,就调用一次,如果只使用类的静态成员,普通代码块不会执行。
- 静态代码块和静态属性初始化
- 普通代码块和普通属性初始化
- 构造器:构造器的最前面隐含了super()和调用普通代码块,因此优先于构造器和普通代码块执行的。
- 父类的静态代码块和静态属性
- 子类的静态代码块和静态属性
- 父类的普通代码块和普通属性
- 父类的构造器
- 子类的普通代码块和普通属性
- 子类的构造器
public class CodeBlockDetail04 {
public static void main(String[] args) {
new B02();
}
}
class A02 { //父类
private static int n1 = getVal01(); //静态属性
static { //静态代码块
System.out.println("A02 的一个静态代码块..");//(2)
}
{ //普通代码块
System.out.println("A02 的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() { //静态方法
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() { //普通方法
System.out.println("getVal02");//(6)
return 10;
}
public A02() { //构造器
//隐藏
//super()
//普通代码和普通属性的初始化......
System.out.println("A02 的构造器");//(7)
}
}
class B02 extends A02 { //子类
private static int n3 = getVal03(); //静态属性
static { //静态代码块
System.out.println("B02 的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{ //普通代码块
System.out.println("B02 的第一个普通代码块..");//(9)
}
public static int getVal03() { //静态方法
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() { //普通方法
System.out.println("getVal04");//(8)
return 10;
}
//一定要慢慢的去品..
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化...
System.out.println("B02 的构造器");//(10)
}
}
单例设计模式
单例
- 单例设计模式就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
- 两种单例模式:饿汉式、懒汉式
- 构造器私有化,防止直接 new
- 类的内部创建对象
- 向外暴露一个静态的公共方法
public class SingleTon01 {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
}
}
//饿汉式
class GirlFriend {
private String name;
private static GirlFriend gf = new GirlFriend("tom");
private GirlFriend(String name) { //私有化构造器
System.out.println("构造器被调用");
this.name = name;
}
public static GirlFriend getInstance() { //方法
return gf;
}
public String toString() {
return "name=" + name;
}
}
public class SingleTon02 {
public static void main(String[] args) {
Cat instance = Cat.getInstance();
System.out.println(instance);
}
}
//懒汉式
class Cat {
private String name;
private static Cat cat; //null
private Cat(String name) { //构造器
System.out.println("构造器被调用");
this.name = name;
}
public static Cat getInstance(){
if (cat == null) {
cat = new Cat("小黑");
}
return cat;
}
@Override
public String toString() {
return "name='" + name;
}
}
饿汉式与懒汉式
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
- 饿汉式存在浪费资源的可能。如果一个对象实例没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,不存在这样的问题。
final(最终的,最后的)可以修饰类、属性、方法、局部变量。
什么情况使用final- 当不希望类被继承时,可以用final修饰。
- 当不希望父类的某个方法被子类覆盖/重写时,可以用final修饰。
- 当不希望类的某个属性的值被修改,可以用final修饰。
- 当不希望某个局部变量被修改,可以用final修饰。
- final修饰的属性又叫常量,一般用大写字母XX_XX_XX 来命名。
- final修饰的属性在定义时必须赋值,并且不能再修改,复制可以在以下位置:
- 定义时
- 构造器中
- 代码块中
- 如果final修饰的属性是静态的,则初始化的位置只能在:①定义时 ②静态代码块中
- final类不能被继承,但可以实例化对象。
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
- final不能修饰构造器,如果一个类已经是final类,就不需要将方法修饰成final方法了。
- 包装类(Integer,Double,Float,Boolean等都是final)String也是final类。
当父类的某些方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,这个类就是抽象类。
抽象类介绍- 用abstract关键字修饰一个类/方法时,这个类就叫做抽象类/抽象方法。
- 抽象类的价值更多作用在于设计,是设计者设计好后,让子类继承并实现抽象类。
- 抽象类不能被实例化。
- 抽象类不一定要包含abstract方法,抽象类可以没有abstract方法。
- 一旦类包含了abstract方法,则这个类必须声明为abstract。
- abstract只能修饰类和方法。
- 抽象方法不能有主体。
- 如果一个类继承了抽象类,则必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
- 抽象方法不能使用private、final、static来修饰。
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
- 接口是更加抽象的抽象类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体,接口体系了程序设计的多态和高内聚低耦合的设计思想。jdk8.0后接口类可以有静态方法、默认方法,接口中可以有方法的具体实现。
- 接口不能被实例化
- 接口中的所有方法是public方法,接口中抽象方法可以不用abstract修饰。
- 一个普通类实现接口,就必须将该接口的所有方法都实现。
- 抽象类实现接口,可以不用实现接口的方法。
- 一个类可以同时实现多个接口。
- 接口中的属性,只能是final的,而且是public static final修饰符。
- 接口中属性的访问形式:接口名.属性名
- 接口的修饰符只能是public 和默认,这点和类的修饰符是一样的。
定义在局部位置(方法中/代码块):①局部内部类(有类名) ②匿名内部类(没有类名)
定义在成员位置:①成员内部类(没用static修饰) ②静态内部类(使用static修饰)
- 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它的地位就是一个局部变量,但可以用final修饰。
- 作用域:在定义它的方法或代码块中。
- 局部内部类可以直接访问外部类的成员。
- 外部类需要创建对象,才能访问外部类成员。
- 外部其他类不能访问局部内部类。
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果要访问外部类的成员,使用(外部类名.this.成员)访问。
public class LocallnnerClass {
public static void main(String[] args) {
Outer01 outer01 = new Outer01();
outer01.m1();
System.out.println("outer01 的 hashcode=" + outer01);
}
}
class Outer01 { //外部类
private int n1 = 100;
private void m2() { //私有方法
System.out.println("Outer01 m2()");
}
public void m1(){ //方法
final class Inner01{ //内部类
private int n1 = 800;
public void f1(){
System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer01.this.n1);
System.out.println("Outer01.this hashcode=" + Outer01.this);
m2();
}
}
//外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner01 inner01 = new Inner01();
inner01.f1();
}
}
匿名内部类
①该类没有名字 ②是一个对象 ③匿名内部类是定义在外部类的局部位置,比如方法或代码块,并且没有类名 ④语法:new 类或接口(参数列表){ 类体 };
细节:
- 匿名内部类既是一个类的定义,同时也是一个对象。
- 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它相当于一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类直接访问外部类成员。
- 外部其他类不能访问匿名内部类。
- 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果要访问外部类的成员,使用(外部类名.this.成员)访问。
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { //外部类
private int n1 = 10;//属性
public void method() {//方法
//基于接口的匿名内部类
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger 的运行类型=" + tiger.getClass());
tiger.cry();
//演示基于类的匿名内部类
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名类重写test方法");
}
};
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("吃东西");
}
};
animal.eat();
}
interface IA {//接口
public void cry();
}
class Father { //类
public Father(String name) { //构造器
}
public void test() { //方法
}
}
abstract class Animal { //抽象类
abstract void eat(); //抽象方法
}
}
成员内部类
- 成员内部类是定义在外部类的成员位置,没有static修饰。
- 可以直接访问外部类的所有成员,包含私有的。
- 可以添加任意修饰符(public、protected、默认、private),因为它相当于一个成员。
- 作用域:和外部类的其他成员一样,为整个类体。
- 成员内部类直接访问外部类成员。
- 外部类需要创建对象再访问成员内部类。
- 外部其他类访问成员内部类。
- 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果要访问外部类的成员,使用(外部类名.this.成员)访问。
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//第一种方法
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//第二种方法 在外部类中,编写一个方法,返回 Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { //外部类
private int n1 = 10;
public String name = "张三";
class Inner08 { //成员内部类
public void say() { //方法
System.out.println("n1 = " + n1 + " name = " + name);
}
}
public Inner08 getInner08Instance() { //方法
return new Inner08();
}
public void t1() { //方法
Inner08 inner08 = new Inner08();
inner08.say();
}
}
静态内部类
- 静态内部类定义再外部类的成员位置,并且用static修饰
- 可以添加任意修饰符(public、protected、默认、private),因为它相当于一个成员。
- 作用域:同其他的成员,为整个类体。
- 静态内部类直接访问外部类成员。
- 外部类需要创建对象再访问静态内部类。
- 外部其他类访问静态内部类。
- 如果外部类和静态内部类的成员重名时,默认遵循就近原则,如果要访问外部类的成员,使用(外部类名.this.成员)访问。
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.hi();
//外部其他类 使用静态内部类 方式1
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式2
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
static class Inner10 { //静态内部类
public void say() { //方法
System.out.println(name);
}
}
public void hi() { //方法
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {//方法
return new Inner10();
}
}



