文章目录
面向对象(OOP)编程
面向对象类的五大成员:属性、方法、构造器、代码块、内部类
方法概述与使用
- 方法定义:
访问修饰符 (static) 返回类型 方法名(形参列表...){方法体;}
- 方法未调用时,存储于方法区/元空间的class文件中;调用后进入栈内存中运行
- 方法定义时用于接收数据的参数称为形参,调用时传入方法的参数称为实参
- 方法体
return
的类型需与返回类型一致,或能自动转换为返回类型- jvm执行方法时,会开辟一个独立的栈空间;return语句执行后,将会执行弹栈
public static void learn(){ System.out.println("加油学习!"); }
public int getRandomNum(){ return (int)(Math.random()*10+1); }
方法重载
- 定义:同一个类中可定义了多个同名方法,但各方法的形参列表(个数、类型)不同,这些方法构成了重载关系
- 重载与返回类型无关
- 方法重载增加了程序的灵活性,避免功能相同的代码重复命名
public int calcu(int n){}
public double calcu(double n){}
//同类中方法重载,方法名必须相同,形参列表必须不同
调用重载的方法时,系统优先选择各参数类型一致的方法进行调用;若无各参数类型完全一致的方法时,系统会选择各参数中实参类型可自动转换为形参类型的方法进行调用
public static boolean equals(int a,int b){return a==b;} public static boolean equals(short a,short b){return a==b;} //int类型先于short类型定义 public static void main(String[] args){ short a = 1, b = 1; boolean result = equals(a,b); //默认先选择equals(short a,short b)执行 //若第二个equals被注释时,系统会调用第一个equas方法 }
public static boolean equals(double a,int b){return a==b;} public static boolean equals(int a,double b){return a==b;} //这种情况下以equals(10,10)调用会导致编译不通过,方法混淆!
类与对象
概念:
- 类是自定义数据类型,是对象的描述,通过new关键字创建对象,同时在方法区加载对应类信息
- 对象是类的实体,存储在堆内存中,其引用值赋予栈内存中的对应的类变量
- 对象中的基本数据类型成员(属性)以值的形式存储于堆内存中,而引用数据类型成员存储于堆内存或常量池中(String)
- 创建成员变量时,会参照数组赋予其默认值
- 对象在堆内存中的存储地址:
全类名@十六进制地址
创建并使用对象的内存流程图:
- 使用该对象类型时,JVM先将类信息加载到方法区中
- new关键字创建对象,存储在堆内存中
- 对象的成员变量直接以值的形式存储于堆内存中;成员方法则保存指向方法区中类信息对应方法的地址的索引
成员变量与局部变量
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中、方法外 | 方法内、方法声明时(形参) |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 伴随对象存亡 | 方法调用生成,方法结束消失 |
初始化值不同 | 默认初始化 | 无初始化,需定义赋值后使用 |
作用域
- 全局变量(属性),作用域为整个类
- 局部变量作用域为所在的方法
- 全局变量可不赋值(默认值)使用,而局部变量无默认值
- 全局与局部变量可重名,访问时遵循就近原则
- 全局变量可跨类使用,局部变量只能作用在所在类代码块中,且全局变量可加修饰符
封装
封装是面向对象三大特征之一(封装、继承、多态)
封装隐藏了实现细节,仅对外暴露公共的访问方法
封装的好处:提高代码的安全性、复用性
封装的常见体现:
private
私有化成员变量,对外提供set、get方法- 将相同逻辑的代码封装为方法,供调用者重复使用
- 将属性抽取到类中,视为对数据的封装
pubic Peoople(String name){ this.setName(name); }
//将构造器与set方法结合,创建对象时进行数据校验
this关键字
- java虚拟机给每个对象分配this,表示该对象
- this可以访问本类的属性、方法、构造器
- this用于区分当前类的全局、局部变量
this(参数列表)
访问构造器,只能在构造器中使用,且放在第一条语句- this只能在类定义的实例方法中使用
- 方法被那个对象调用,this就指向那个对象
构造器Constructor
- 构造器是创建对象时调用的方法,完成类对象的初始化
- 构造器修饰符可为默认,可为其他
- 构造器没有返回类型、返回值
- 构造器方法名一定与类名一致
- 参数列表和成员方法一样规则
- 构造器由JVM调用,无法手动调用
- 若无创建构造器,系统默认定义一个无参构造器
static关键字
静态变量
- 被static修饰的变量,被称为静态变量、类变量
- 静态变量存储在方法区的类信息中,被所有的对象共享
- 静态变量可使用类名或方法名访问,推荐使用类名
- 格式:
static 数据类型 变量名
静态方法:
- static修饰的方法,被称为静态方法
- 静态方法可通过类名、对象名访问;本类中调用本类的类方法,可通过方法名直接访问
- 静态方法只能访问静态成员,且不能使用this、super关键字
代码块
- 代码块又称初始化块,类中的成员,类似于方法,将逻辑语句封装在方法体中,以
{}
包围- 代码块与方法不同之处在于其没有方法名、返回值、参数,不可通过类名、对象名显式调用,只能在类加载、创建对象时被隐式调用
- 创建对象时,优先级:静态属性初始化、静态代码块 > 普通属性初始化、普通代码块 > 构造器
构造/普通代码块:
- 格式:
{}
- 特点:构造器被调用时,先执行构造代码块再执行构造器中的语句,相当于对构造器的补充机制
- 使用场景:可做初始化的操作,可提取多个构造器中相同代码到构造代码块中,以提高代码复用
静态代码块:
- 结构:
static{}
- 特点:随着类加载而执行,且仅一次;
- 使用场景:类加载时,做一些静态数据初始化操作,供后续对象使用
class A{
//第一创建B对象时A类先加载,静态代码块被调用
public A{
//第四步调用父类普通代码块,再执行构造器中代码
}
}
class B extends A{
//第二创建B对象时A类加载完后B类加载,静态代码块被调用
public B(){
super();//第三步调用父类构造器
//第五步调用子类普通代码块
//第六步执行子类构造器中代码
System.out.printf("B构造器~");
}
}
继承
- 作用:将多个类相同成员抽取到一个单独类中,多个类只需继承该单独类即可使用相同成员,以此提高代码复用性
- 父类(超类):共有属性,共有方法
- 子类(派生类):特有属性,特有方法
- 格式:
class 子类名 extends 父类名{}
- java只支持单继承,但支持多层继承
继承的特点:
- 子类继承父类所有属性与方法,但私有的属性与方法不可于子类中直接访问,可通过父类的公共方法访问==(java官方文档:子类不能继承父类私有属性,但如果子类中的公有方法影响到父类私有属性,那么私有属性可被子类使用)==
- 在一个子类被创建时,首先会在内存中创建一个父类对象,然后在父类对象的外部放上子类独有的属性,这两者结合起来形成了一个子类的对象。子类对象确实拥有父类对象中的所有属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用
- 子类不可继承父类的构造方法,但子类构造器中必调用父类的构造器,完成父类的初始化;默认调用父类的无参构造器,需借super去指定使用那个父类的构造器完成父类的初始化;若父类没提供无参构造器,需在子类中用super指定父类的其他构造器,否则编译失败
- 继承关系下子类中同名的变量和方法的访问,遵循就近原则:先在方法中查找,其次在本类中查找,最后在父类中查找
- 可使用super,明确访问父类成员
方法重写规则:
- 方法名和参数列表必须与父类一致
- 子类重写的方法,访问权限必须大于等于父类
- 子类方法返回值类型必须与父类方法返回值类型相同,或为其子类
- 子类不可重写父类private私有方法
- 静态方法可继承,不可覆盖重写,若子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。
@Override注解作用:
- 添加在重写方法的上方,可检验重写格式
super关键字
this代表本类对象的引用
super代表本类对象中的父类储存空间的标识
super作用:
- 当子类有与父类同名的属性、方法,可以使用super访问父类中的该属性、方法
- super无法直接访问父类的private属性、方法
- super()访问父类的构造器,只能放在子类构造器第一句,同一构造器中只能仅存在super()/this();父类属性由父类初始化,子类属性由子类初始化,分工明确
- super可以理解为子类对象中的父类对象,其类型为父类类型,同时指向父类方法表。
权限修饰符
权限修饰符控制成员能够被访问的范围,可修饰
- 成员变量
- 方法
- 构造器
- 类
修饰符 同包同类 同包不同类 不同包子类 不同包无关类 private √ 默认(缺省) √ √ protected √ √ √ public √ √ √ √
final关键字
final的用法:
- final修饰的类为最终类,不可继承
- final修饰的方法为最终方法,不可重写
- final修饰的变量为常量,不可改变
- final修饰的方法,JVM会将其内联,提高执行效率
- final修饰的常量,会在编译阶段存入常量池中
抽象类
抽象类概述:
- 抽象类是对子类共同特征的抽取,可以将多个子类中相同的属性和行为抽取到父类中,某些功能定义成抽象方法,父类只需提供一个方法签名即可,无需方法体,即抽象方法。子类可以重写抽象方法实现功能,以达到简化代码,复用功能的目的。
- 抽象类不一定有抽象方法,有抽象方法的类一定为抽象类。
- 抽象类具有普通类的所有特性,除创建实例对象
- 子类必须重写父类的所有抽象方法,除非子类也是抽象类
抽象类定义格式:
权限修饰符 abstract class 类名{}
抽象方法定义格式:
权限修饰符 abstract 返回值类型 方法名(形参列表);
注意事项:抽象方法不可被private、static、final修饰,因为意义冲突
接口
接口概述:
- 接口是比抽象类更为彻底的抽象,对其实现类的功能进行规范,实现接口的目的是为了扩展类的功能
接口定义格式:
public/默认 interface 接口名{}
接口用法:
- java中支持类多实现接口,实现类需重写接口中所有抽象方法
- 格式:
权限修饰符 class 类名 implements 接口A,接口B{}
- 接口可多继承其他接口,需遵从规范
JDK7之前成员:
- 常量:接口中所有成员变量默认
public final static
修饰,可省略- 抽象方法:接口中所有方法默认
public abstract
修饰,可省略
JDK8之后成员:
- 默认方法:
public default 返回值类型 方法名(形参列表){}
,默认public修饰可省略,需由实现接口的类对象调用- 静态方法:
public static 返回值类型 方法名(形参列表){}
,默认public修饰可省略,必须用接口名形式调用- 私有方法:
private (static) 返回值类型 方法名(形参列表){}
,私有方法只能在接口中被其他方法访问
接口的注意事项:
- 一个类同时继承父类并实现接口,父类中成员方法与接口中默认方法相同时,子类就近调用父类的成员方法
- 实现的多个接口中有相同的抽象方法,实现类只需重写一次即可
- 实现的多个接口中有相同的默认方法,实现类(多继承接口)需重写方法
- 实现类对象想调用实现的接口中的默认方法:
接口名.super.默认方法名();
- 实现的多个接口中有相同的静态方法,不会造成冲突,因静态方法通过接口名访问
接口与抽象类的区别
相同:
- 接口和抽象类都可以定义抽象方法。
- 接口和抽象类都不能创建对象。
不同:
- 抽象类可以定义成员变量,构造方法;接口中的变量只能是public static final修饰的常量,且不能有构造方法
- 抽象类可以被子类继承,而且只能单继承;接口可以被子类实现,而且支持多实现;接口也可以被其他接口继承,而且是多继承。
模板设计模式
//多个类中有需完成不同的功能,但功能中有大部分相同代码
class Job1 extends Job{
public void work(){//重写抽象类Job中的work抽象方法
……
}
}
class Job2 extends Job{
public void work(){
……
}
}
abstract Job{
public abstract void work();//子类不同work方法定义为抽象
public void caleTime(){//统计时间的方法在抽象类中实现
long start = System.currentTimeMills();
work();
longe end = System.currentTimeMills();
System.out.println(start - end);
//统计work完成时间
}
}
public class Test{
public static void main(String[] args){
Job1 job1 = new Job1();
job1.caleTime();
Job2 job2 = new Job2();
job1.caleTime();
}
}
多态
概述:多态建立在封装与继承之上,方法和对象可具有多种形态,是面向对象的第三大特征
多态在java中体现形式:
- 父类引用指向子类的实例对象
Animal animal = new Dog();
- 接口引用指向实现类的实例对象
多态前提:
- 有继承/实现关系
- 有方法重写
- 父类/接口引用指向子类/实现类对象
多态内容:
- 引用的编译类型与运行类型可不一致,编译类型看
=
左边,运行类型看=
右边- 编译类型在定义对象变量时确定,不可改
- 运行类型可变化,可用instanceof判断对象的运行类型
多态成员访问特点:
- 属性不可重写,可隐藏,故其无多态性,属性有编译类型决定(静态绑定)
- 方法可重写,编译时查找编译类型方法表,运行时查找运行类型方法表,故调用的实际都是运行类型(子类/实现类)重写的方法(动态绑定)
多态使用场景:
- 父类引用保存子类对象地址
- 父类返回类型,返回子类对象
- 父类做形参类型接收,实际传入子类对象
向上转型:
- 父类型引用可指向子类型对象,
Father a = new Son();
- 向上转型后子类型特有属性、方法不可用,能调用的属性、方法是由编译类型决定的。
向下转型:
- 父类型引用(保存子类型对象地址值)可以强转为子类型,并赋予子类类型引用,
Son b = (Father)a;
- 只能强转父类保存子类的引用,不可强转父类对象
- 若父类型引用保存的子类型的类型,与目标类型不一致,会报ClassCastException异常,可用instanceof验证后再执行向下转型
- 向下转型后,可调用子类型中所有成员
动态绑定机制
- 调用对象方法时,该方法会和该对象的内存地址、运行类型绑定
- 调用对象属性时,没有动态绑定,哪里声明,哪里使用
内部类
概念:类内部嵌套另一个类,被嵌套的类称为内部类,嵌套其他类的类称为外部类,内部类最大特点是内部类可以直接访问外部类私有属性
静态内部类
概述:静态内部类是定义在外部类中,以static关键字修饰的类
语法:
class Outer{ 权限修饰符 static class Inner{} }
创建对象格式:
Outer.Inner 对象名 = new Outer.Inner();
- 可外部类中定义一个方法,返回内部类实例对象
特点:
- 普通类可具备的成员,静态内部类一样具备
- 直接访问外部类全部静态成员(包括私有),但不可直接访问非静态成员
- 间接在内部类中创建外部类时,可以访问其非静态成员变量
- 内部类中有静态方法或者静态成员变量时,一定是静态内部类
- 外部类和内部类成员重名时遵循就近原则,若需访问外部类非静态成员,可(外部类名.this.成员名)来访问,若外部类成员为静态,可(外部类名.成员名)来访问
成员内部类
概述:无static修饰的内部类,可用四种权限修饰符
语法:
class Outer{ 权限修饰符 class Inner{} }
创建对象格式:
Outer.Inner 对象名 = new Outer().new Inner();
特点:
- 直接访问外部类全部成员(包括私有)
- 作用域为整个外部类,外部类成员方法中可创建成员内部类对象,再调用其方法
- 外部类访问内部类成员时,需先创建对象后访问
- 成员内部类中不可以定义静态成员(静态常量可以),可从类加载的角度理解
- 外部类和内部类成员重名时遵循就近原则,若需访问外部类成员,可(外部类名.this.成员名)来访问
局部内部类
概述:局部内部类定义在方法、代码块、构造器中
特点:
- 作用域只在定义他的方法、代码块中
- 不用权限修饰符,可final修饰
- 可访问外部类所有成员(包括私有),但只能访问final修饰的局部变量
- 其他外部类不可访问局部内部类,理解为局部成员其他类不可访问
匿名内部类
概述:未定义类名的类,定义类同时创建其实例对象
语法:
new 类或接口(形参列表){ //重写方法 } ;
特点:
匿名内部类具有局部内部类的特性
实际底层声明内部类形式:
class 外部类名$n extends/implament 类名/接口名(){ //重写方法 }
$符号后的n为该匿名类在外部类中顺序(第n个匿名类)
若匿名内部类为某父类的子类,参数列表会传入父类的构造器
匿名内部类通常作为方法实参使用,目的是实现父类或接口中抽象方法