1.类和对象的定义
- 1.
类
:指对现实中对象的共性抽取,通常用来表示同一批具有相同特征和行为的对象- 2.
对象
:指类的实例化,其是计算机内存中的一块存储空间,用来存储现实中对象特性和行为的描述- 3.类和对象的关系
- 1.类是对象的
模板
- 2.对象是类的
实例
2.类
- 1.
属性
:用来描述对象的特征
局部变量 成员变量(属性) 位置 形参或方法、代码块内部 类的内部、方法的外部 默认值 没有 有 作用范围 从定义开始到定义的{}结束 当前整个类 命名冲突 局部变量之间不能重名 属性可以和局部重名,局部变量优先更高 - 2.
方法
:用来描述对象行为方法修饰符 返回值类型 方法名(参数列表){ // 操作语句 }
- 3.规则:
- 1.一个源文件中只能有一个
public
类- 2.一个源文件可以有多个非
public
类- 3.源文件的名称应该和
public
类的类名保持一致- 4.如果一个类定义在某个包中,那么
package
语句应该在源文件的首行- 5.如果源文件包含
import
语句,那么应该放在package
语句和类定义之间;如果没有package
语句,那么import
语句应该在源文件中最前面- 6.
import
语句和package
语句对源文件中定义的所有类都有效;同一源文件中不能给不同的类不同的包声明
1.属性
类变量 实例变量 修饰符 static关键字 无 访问方式 类或类对象 类的对象 生存周期 静态成员不依赖于类的特定实例,被类的所有实例共享;static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,Java 虚拟机就可以根据类名找到它们 与对象同存亡,被对象调用后才分配内存,调用结束时内存释放 加载时机 运行时Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配 每创建一个实例,Java 虚拟机就会为实例变量分配一次内存
- 1.类的属性分为(具体可参考
基本语法中的变量的作用域
)
- 1.类变量/静态变量(
static
修饰)- 2.实例变量
2.方法
- 1.构造方法
- 2.静态方法
- 3.普通方法
1.方法重载
- 1.
定义
- 1.方法重载:指在同一个类中定义多个方法,它们具有相同的名称但参数列表不同,根据
不同的传参
来执行不同的逻辑处理
- 2.
规则
- 1.重载的方法必须具有相同的方法名
- 2.参数列表必须不同(参数类型,个数,顺序)
- 2.
访问修饰符
,返回类型
,异常
不影响方法重载
2.方法重写
- 1.
定义
- 1.方法重写:指子类重新定义父类中已有的方法,以实现特定的功能
- 2.当子类对父类方法进行重写之后,子类对象调用方法执行的是子类重写的内容
- 2.
规则
- 1.必须存在于继承关系中
- 2.重写的方法必须与父类方法具有相同的方法名
- 3.参数的类型、个数和顺序必须相同
- 4.返回类型必须与父类方法相同或是其子类
- 5.子类方法的访问修饰符不能比父类方法更严格(如父类方法是
protected
,子类方法可以是public
,但不能是private
)- 6.子类不允许抛出比父类范围更大的异常
- 7.私有方法,构造方法,静态方法无法被重写
- 1.静态方法属于类,不属于实例,重写是针对
实例方法
而静态方法不是实例方法,因此不能重写;其次静态方法在编译
时绑定到类,而不是在运行
时绑定到实例,重写依赖于动态绑定,因此不能重写;静态方法通过类名调用,而不是通过实例调用,即使子类定义了与父类相同的静态方法,调用时仍然是根据引用类型决定调用哪个方法,而不是根据实际对象类型- 2.私有方法的访问权限决定了其只能在定义的类内部访问,子类无法访问父类的私有方法,因此也无法重写它
- 3.构造方法是用于对象初始化的特殊方法不属于类的实例方法,重写是针对实例方法,而构造方法不是实例方法,因此不能重写;构造方法在创建对象时由
JVM
自动调用,子类的构造方法必须显式或隐式地调用父类的构造方法,如果允许重写构造方法,子类可能会绕过父类的初始化逻辑,导致对象状态不一致;构造方法的名称必须与类名相同,而重写要求方法名相同,子类的类名与父类的类名不同,因此无法满足重写的条件- 3.
方法重写与重载的区别
3.this()方法
- 1.用于在当前类的构造方法中调用另一个构造方法
- 2.只能写在
构造方法
有效代码第一行
- 3.由
this()
实参列表决定调用哪个构造方法- 4.构造方法无法通过
this()
调用自身,这样会成为死循环调用从而导致内存溢出
- 5.构造方法之间也不能
相互循环调用
,否则会死循环从而导致内存溢出- 6.应用场景
- 1.构造方法重载:通过
this()
调用当前类的其他构造方法,避免代码重复- 2.初始化逻辑复用:将通用的初始化逻辑放在一个构造方法中,其他构造方法通过
this()
调用
4.super()方法
- 1.用于在子类的构造方法中调用父类的构造方法
- 2.只能写在子类
构造方法
有效代码第一行
- 3.由
super()
实参列表决定调用哪个父类构造方法- 4.
this()
和super()
不能同时出现(因为this()
和super()
必须作为构造方法的第一条语句)- 5.如果
子类构造方法
没有显式调用父类构造,那么无参的super()
默认存在于子类构造方法第一行(编译器自动插入)- 6.当子类构造显式调用
this()
时,当前构造方法就只能使用默认的无参super()
- 7.如果父类没有无参构造方法,子类必须显式调用父类的有参构造方法
- 8.应用场景
- 1.初始化父类状态:确保父类的状态被正确初始化
- 2.调用父类构造方法:在子类构造方法中显式调用父类的构造方法
5.构造方法
- 1.定义:构造方法是一种特殊的方法,用于在创建对象时初始化对象的状态
- 2.构造方法的
访问修饰符
只能使用public
,且构造方法名和类名
必须一致- 3.没有返回类型(包括
void
)- 4.使用
new
关键字来调用构造方法- 5.当类中没有提供
显式构造方法
,默认提供一个无参数的构造方法- 6.当类中提供了
显式构造方法
,将不再默认提供无参数构造方法- 7.建议将无参数构造方法显示声明,这样一定不会出问题
- 8.构造方法支持
方法重载
,不支持方法重写
- 9.子类构造方法必须调用父类的构造方法(显式或隐式)
6.静态方法和普通方法
- 1.
定义
: 使用static
修饰的方法称为静态方法,静态方法属于类,而不是类的实例- 2.
生命周期
:静态方法在类加载时分配内存(方法区
),程序结束时释放内存,所有实例共享同一个静态方法;普通方法在对象创建时分配内存(方法区
),对象销毁时释放内存,每个实例都有自己的普通方法调用栈,通方法的调用依赖于实例,因此需要通过实例来访问- 3.作用范围:
静态方法
只能访问静态数据
(静态变量和静态方法),非静态方法
既可以访问静态数据
又可以访问非静态数据
- 4.因为
静态方法
和静态数据
会随着类加载而被加载到内存中,而非静态方法
和非静态数据
只有在对象创建时
才会加载到内存中,如果静态方法
调用了了非静态数据
,则在静态方法加载时无法从内存中找到非静态数据
,势必会出错,这种做法是Java虚拟机
不允许的- 5.调用方式:引用静态方法时,可以用
类名.方法名
或者对象名.方法名
的形式- 6.静态方法中不能使用
this
和super
关键字,因为这两个关键字都依赖于对象
7.代码块和静态代码块
class 类名{ static{ // 静态初始代码块 } { // 普通代码块 } }
- 1.定义:
static
修饰的代码块称为静态代码块
- 2.
静态代码块
只有在类加载
时才会执行,且只执行一次,一个静态代码块
没有执行完成时不会执行下一个静态代码块
,静态代码块属于类,而不是实例- 3.
普通代码块
在创建对象时执行,每次创建对象时都会执行,且在构造方法之前执行,代码块属于实例- 4.
执行顺序
:静态代码块
>普通代码块
>构造函数
- 5.
静态代码块
只能访问类中的静态成员变量
- 6.
普通代码块
只能访问类中的成员变量
- 7.
位置
:两者一般都写在属性之下,方法之上,静态代码块类似于一个方法,但它不可以存在于任何方法体中,静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块- 8.
作用
:静态代码块一般是为了给静态成员变量赋值,一般将一些只需要进行一次的初始化操作都放在静态代码块中进行;代码块一般是为了给成员变量赋值- 9.如果类中包含多个静态代码块,则
Java
虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次- 10.静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过
类的实例对象
来访问,静态代码块和普通代码块的字节码都存储在方法区
中- 11.代码块的应用场景
- 1.初始化实例变量:代码块在构造方法之前执行,用于初始化实例变量
- 2.复用初始化逻辑:多个构造方法共享相同的初始化逻辑
- 12.静态代码块的应用场景
- 1.初始化静态变量:在类加载时执行,用于初始化静态变量。
- 2.加载静态资源:在类加载时加载静态资源(如配置文件、数据库连接等)
8.方法返回值
- 1.指获取到的某个方法体中的
代码执行后产生的结果
- 2.返回值的作用是接收结果,使得其可以用于其他的操作
- 3.方法返回值可以是
基本数据类型
,也可以是引用数据类型
,void
表示方法没有返回值
3.类加载机制
- 1.参考
JVM及GC
文章中的类加载子系统
3.抽象类
- 1.用
abstract
修饰的类- 2.抽象类不能
实例化对象
,设计理念是为了服务其他子类,所以规定不能实例化- 3.其它功能和普通类一致
- 1.可以有构造方法,用于初始化成员变量
- 2.可以有成员变量:抽象类可以定义成员变量
- 3.可以有具体方法:抽象类可以定义具体方法(有实现的方法)
- 4.可以有静态方法:抽象类可以定义静态方法
- 4.由于抽象类不能实例化对象,所以
抽象类必须被继承才能被使用
- 5.
Java
中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口- 6.
private
,static
,final
不能与abstract
连用,因为abstract
需要被继承实现且无法被实例化
- 1.如果
static
和abstract
连用,会导致语义矛盾:static
方法可以直接调用,但abstract
方法没有实现,无法调用- 2.
static
方法的调用不依赖于实例,而abstract
方法的实现依赖于子类,如果允许static
和abstract
连用,会导致调用static
方法时无法确定具体的实现- 7.应用场景
- 1.定义通用行为:抽象类可以定义一组通用的行为(具体方法),子类可以直接继承这些行为
- 2.强制子类实现特定行为:抽象类可以定义抽象方法,强制子类实现特定的行为
- 3.代码复用:抽象类可以将通用的代码放在具体方法中,子类可以直接复用这些代码
1.抽象方法
- 1.用
abstract
修饰的方法- 2.抽象类中的抽象方法只有声明,没有方法体
- 3.抽象方法具体实现由它的子类实现
- 4.抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
- 5.任何
子类必须重写父类的抽象方法
,除非子类也是抽象- 6.最终必须有子类实现该抽象方法,否则从最初的父类到最终的子类都不能用来
实例化对象
- 7.私有方法,构造方法,静态方法(用
static
修饰的方法)不能声明为抽象方法
- 1.语义矛盾:构造方法用于初始化对象,而抽象方法没有实现,无法完成初始化,如果构造方法是抽象的,子类无法调用父类的构造方法,导致对象无法正确初始化
- 2.设计原则:构造方法必须在对象创建时执行,而抽象方法需要在子类中实现,这两者是矛盾的
4.内部类
- 1.
定义
:内部类是定义在另一个类内部的类- 2.根据定义位置和修饰符不同,内部类可分为
- 1.成员内部类
- 2.静态内部类
- 3.局部内部类
- 4.匿名内部类
- 3.内部类的
优缺点
- 1.优点:可实现回调接口、封装隐藏实现细节、简化代码结构和实现类似多继承的效果
- 2.缺点:可能会增加代码的复杂性,如果滥用内部类,可能会导致代码难以理解和维护
- 4.内部类的
生命周期
- 1.成员内部类的生命周期依赖于外部类的生命周期
- 2.静态内部类的生命周期独立于外部类的生命周期
- 3.局部内部类的生命周期仅限于定义它的方法或构造器的执行过程
- 4.匿名内部类的生命周期依赖于它的父类或接口的生命周期
- 5.内部类是否有
静态成员
- 1.成员内部类和匿名内部类不能有静态成员,因为它们依赖于外部类的实例
- 2.局部内部类不能有静态成员,因为其生命周期仅限于定义它的方法或构造器
- 3.静态内部类可以有静态成员,因为它独立于外部类的实例
- 6.
应用场景
- 1.实现回调接口:事件处理中使用内部类来实现事件监听器接口,当事件发生时内部类的方法会被调用,从而实现回调
- 2.封装隐藏实现细节:内部类可以隐藏实现细节,只对外暴露必要的接口,这样可以提高代码的封装性和安全性,如将一些辅助类或工具类定义为内部类,只在外部类中使用,而对外部隐藏这些类的实现细节
1.成员内部类
class 外部类名{ class 内部类类名{ } }
class Outer { private int outerVar = 10; class Inner { void display() { System.out.println("OuterVar: " + outerVar); } } }
- 1.定义:类的内部,方法的外部,与属性和方法平级
- 2.成员内部类可以访问外部类的
所有成员属性
和成员方法
(包括private
成员和静态成员)- 3.成员内部类不能定义
静态内容
,但是可以访问外部类静态内容
(由于成员内部类的实例化依赖于外部类的实例,因此在内部类中定义静态成员会导致语义上的冲突,静态成员属于类级别,不依赖实例,因此成员内部类可以直接访问)- 4.如果外部类属性,内部类属性,内部类局部变量出现重名
- 1.外部类属性:
外部类类名.this.属性名
- 2.内部类属性:
this.属性名
- 3.局部变量:
属性名
- 5.内部类对象的创建需要依赖
外部类对象
外部类类名.内部类类名 内部类对象名 = 外部类对象名.new 内部类类名
- 6.注意
- 1.当
成员内部类
拥有和外部类
同名的成员变量或者方法时,会发生隐藏现象
,即默认情况下访问的是成员内部类
的成员,如果要访问外部类的同名成员,需要通过以下形式进行访问
- 1.
外部类.this.成员变量
- 2.
外部类.this.成员方法
- 2.虽然
成员内部类
可以无条件地访问外部类的成员
,但外部类
想访问成员内部类的成员
必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
2.静态内部类
class 外部类类名{ static class 内部类类名{ } }
class Outer { private static int staticVar = 20; static class StaticInner { void display() { System.out.println("StaticVar: " + staticVar); } } }
- 1.定义:使用
static
修饰符定义在类内部的类。它可以访问外部类的静态成员,但不能访问外部类的非静态成员(静态内部类的生命周期独立于外部类的实例,它不需要外部类的实例就可以被创建和使用,因此静态内部类无法访问外部类的非静态成员,因为非静态成员需要一个具体的外部类实例才能被访问)- 2.如果外部类属性,内部类属性,内部类局部变量出现重名
- 1.访问外部类静态属性:外部类类名.属性名
- 2.访问内部类静态属性:外部类类名.内部类类名.属性名
- 3.静态内部类对象创建要依赖于
外部类类名
,可直接通过外部类类名.内部类类名.静态内容
的方式直接访问内部类静态内容
3.局部内部类
class 外部类类名{ 访问修饰符 返回值类型 方法名(参数列表){ class 内部类类名{ } } }
class Outer { void method() { class LocalInner { void display() { System.out.println("Local inner class"); } } LocalInner localInner = new LocalInner(); localInner.display(); } }
- 1.定义:定义在方法或构造器中,它的作用域仅限于定义它的方法或构造器,局部内部类不能有访问修饰符(如
public、private
等),因为它不是类的成员- 2.无法定义
静态内容
- 3.可以访问外部类的
局部变量
,但是该局部变量必须是常量
(jdk7
之前必须用final
显式修饰,jdk8
之后只需要没有再次更改值)- 4.局部内部类对象只能在
所属的外部类方法
中创建- 5.局部变量存储在方法的栈帧中,而局部内部类的对象可能在方法执行完毕后仍然存在(如局部内部类的对象被传递到其他方法或存储在某个字段中),如果局部变量不是
final
,那么在方法执行完毕后,局部变量的值可能会被修改,而局部内部类的对象仍然持有对该变量的引用,这会导致不一致的行为class Outer { void method() { int localVar = 10; class LocalInner { void display() { System.out.println(localVar); // 如果localVar不是final,可能会导致问题 } } LocalInner localInner = new LocalInner(