文章目录
面向对象 - 上
面向对象学习的三条主线
* 1.Java 类及类的成员:属性、方法、构造器;代码块、内部类
*
* 2.面向对象的特征:封装性、继承性、多态性、(抽象性)
*
* 3.其它关键字:this、super、static、final、abstract、interface、package、import等
类和对象
- 类:对一类事物的描述,是抽象的、概念上的定义
- 对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
面向对象概念
面向对象程序设计的重点是类的设计
设计类,就是设计类的成员
类的设计
1.创建类,设计类的成员
2.创建类的对象: 类名 对象名 = new 构造器名();
3.通过“对象.属性”或“对象.方法”调用对象的结构
设计类的口诀
属性对应特点,方法对应行为
JVM内存解析
- 虚拟机栈,即为平时提到的栈结构。我们将局部变量存储在栈结构中
- 堆,我们将new出来的结构(比如:数组、对象)加载在堆空间中。补充:对象的属性(非static的)加载在堆空间中
- 方法区:类的加载信息、常量池、静态域
类结构之一:属性
在类中通过成员变量(全局变量)来体现 (类中方法外面的变量)对象的特点
属性赋值顺序
① 默认初始化
② 显式初始化 / ⑤在代码块中赋值
③ 构造器中初始化
④ 通过"对象.方法" 或 "对象.属性"的方式赋值
以上操作的先后顺序:① - ② / ⑤ - ③ - ④
对比:成员变量 (属性) vs 局部变量
相同点:
1.1 定义变量的格式:数据类型 变量名 = 变量值 ;
1.2 先声明,后使用
1.3 变量都其对应的作用域
不同点:
2.1 在类中声明的位置的不同
成员变量:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.2 关于权限修饰符的不同
成员变量:可以在声明成员变量时,指明其权限,使用权限修饰符
局部变量:不可以使用权限修饰符。
常用的权限修饰符:private、public、缺省、protected —>封装性
2.3 默认初始化值的情况:
属性:类的属性,根据其类型,都默认初始化值。
整型(byte、short、int、long:0)
浮点型(float、double:0.0)
字符型(char:0 (或’\u0000’))
布尔型(boolean:false)
引用数据类型(类、数组、接口:null)
局部变量:没默认初始化值,意味着,我们在调用局部变量之前,一定要显式赋值
特别地:形参在调用时,我们赋值即可
2.4 在内存中加载的位置:
属性:加载到堆空间中 (非static)
局部变量:加载到栈空间
类结构之二:方法
在类中通过成员方法来体现,表示对象能够做什么行为,描述类应该具的功能
方法的声明
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
说明
1、Java规定的4种权限修饰符:private、public、缺省、protected -->封装性
2、返回值类型: 返回值 vs 没返回值
如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,在方法中,需要使用**return**关键字来返回**指定类型**的变量或常量:“return 数据”。
如果方法没返回值,则方法声明时,使用void来表示。通常,没返回值的方法中就不需要使用return。如果使用了的话,表示结束此方法
3、形参列表: 方法可以声明 0个,1个,或多个形参
格式:数据类型1 形参1, 数据类型2 形参2,...
可变个数形参的方法
格式:数据类型 … 变量名
- 当调用可变个数形参的方法时,传入的参数个数可以是:0个、1个、2个。。。
- 可变个数形参在方法的形参中,必须声明在末尾
- 可变个数形参在方法的形参中,最多只能声明一个可变形参
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载,形参类型相同的数组之间不构成重载
值传递机制
- 方法内变量的赋值
- 如果变量是基本数据类型,此时赋值的是变量所保存的数据值
- 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
- 参数传递机制:值传递
- 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
- 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
递归方法
定义:一个方法体内调用它自身
注意
1、方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
2、递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
方法的重载
定义:
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
注意
重载跟方法的权限修饰符、返回值类型、形参变量名、方法体都没关系
方法的重写
定义:
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
应用:
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法
要求
- 1.方法名和参数列表相同
- 2.子类方法的访问权限必须大于等于父类的访问权限
- 特殊情况:子类不能重写父类中声明为private权限的方法
- 3.方法重写时:若父类中返回值类型是void或基本数据类型(int),则子类只能是void或基本数据类型(int),要么是父类返回值类型的子类
- 4.子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
- 4.方法重写之后,可以在父类的方法基础上做扩展。
如何在父类的基础上做扩展:super关键字
super 表示父类的对象。 在子类中可以通过super来调用父类的方法和属性
重载和重写的区别
1.重写一定是发生在继承 (子类和父类之间) 的基础之上,重载是发生在同一个类上的关系
2.重写是方法名相同,参数列表也相同。重载是方法名相同,参数列表不同
3.重写时,子类的方法的访问权限必须大于或者等于父类访问权限。重载对访问权限没要求
4.重写时,子类的方法返回值类型必须与父类的方法返回值类型相同或者时父类返回值类型的子类。重载对返回值都没要求
类结构之三:构造方法
构造器(或构造方法):Constructor
构造器的作用:
1、创建对象
2、初始化对象的信息
使用说明:
1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.定义构造器的格式:权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载
4.一旦显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器
JavaBean
所谓JavaBean,是指符合如下标准的Java类:
>类是公共的
>一个无参的公共的构造器
>属性,且对应的get、set方法
类的结构之四:代码块
代码块 (初始化块) 作用:用来初始化类、对象的信息
分类:代码块要是使用修饰符,只能使用static
分类:静态代码块 vs 非静态代码块
- 静态代码块
内部可以输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
- 非静态代码块
内部可以输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:由父及子,静态先行
类的结构之五:内部类
内部类的定义:
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
内部类的分类:
成员内部类(静态、非静态 ) vs 局部内部类(方法内、代码块内、构造器内)
- 成员内部类
一方面,作为外部类的成员:
调用外部类的结构
可以被static修饰
可以被4种不同的权限修饰
另一方面,作为一个类:
类内可以定义属性、方法、构造器等
可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
可以被abstract修饰
- 局部内部类:
局部内部类定义位置
局部内部类是在方法中定义的类,和局部变量一样,是不能有权限修饰符public之类的
局部内部类访问方式
局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
常用于返回一个实现了接口(例 Comparable)的类的对象
//返回一个实现了Comparable接口的类的对象 public Comparable getComparable(){ return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; }
注意
在局部内部类的方法中如果要调用局部内部类所声明的方法中的局部变量的话,要求此局部变量声明为final的
jdk 7及之前版本:要求此局部变量显式的声明为final的
jdk 8及之后的版本:可以省略final的声明
注意
成员内部类和局部内部类,在编译以后,都会生成字节码文件
格式:成员内部类:外部类 内 部 类 名 . c l a s s 局 部 内 部 类 : 外 部 类 内部类名.class 局部内部类:外部类 内部类名.class局部内部类:外部类数字 内部类名.class
OOP特性之一:封装
封装的体现:
- 将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
- 不对外暴露的私有的方法
- 单例模式(将构造器私有化)
Java规定的四种权限修饰符
权限从小 —>大
private < 缺省 < protected < public
修饰范围
修饰结构
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
OOP特征之二:继承
为什么要有类的继承性?
1、减少了代码的冗余,提高了代码的复用性
2、便于功能的扩展
3、为多态性的使用,提供了前提
继承性的格式:
class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
继承性的说明:
1、一个类可以被多个子类继承
2、创建子类对象时,会先调用父类的构造方法
3、Java中类的单继承性:一个类只能有一个父类
4、子类继承父类以后,就获取了父类中声明的属性和方法
5、子类直接继承的父类称为:直接父类;间接继承的父类称为:间接父类
6、子类继承父类后,还可以声明自己特有的属性或方法:实现功能的拓展
7、特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然 获取了父类中私有的结构。只因为封装性的影响,其属 性或方法隐藏了,使得子类不能直接调用父类的私有结构
子类对象实例化全过程
从结果上来看:继承性
子类继承父类以后,就获取了父类中声明的属性或方法
创建子类的对象,在堆空间中,就会加载所父类中声明的属性
从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用
Object类
- Object类是所Java类的根父类
- 如果我们没显式的声明一个类的父类的话,则此类继承java.lang.Object类
- 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
- 意味着,所的java类具有java.lang.Object类声明的功能(属性和方法)
- Object类只声明了一个空参的构造器
Object类常用方法:
1、equals()方法:
public boolean equals(Object obj) {
return (this == obj);
}
说明:
①、只能适用于引用数据类型
②、和==(比较基本数据类型时是比较值,比较引用类型时是比较地址值)的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
③、像String、Date、File等包装类等都重写了equals()方法。重写后,比较的不是地址是否相同,而是两个对象的"实体内容"是否相同
④ 通常情况下,我们自定义的类如果使用equals()的话,通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写
重写的原则:比较两个对象的实体内容是否相同
public boolean equals(Object o){
if(this == o) return true;
if(o instanceof Person){
Person p = (Person)o;
return age==p.age && name.equals(p.name);
}
return false;
}
2、toString()方法:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
说明:
①当我们输出一个对象的引用时,实际上就是调用当前对象的toString(),默认返回地址值
③、像String、Date、File等包装类等都重写了equals()方法。重写后,返回对象的"实体内容"
③自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
3、getClass()获取父类 hashCode() 哈希码值
clone()克隆对象 finalize() 垃圾回收
wait()线程等待 notify() 线程唤醒(一个) notifyAll() 线程唤醒(所有)
OOP特征之三:多态
多态性的理解:
可以理解为一个事物的多种形态
何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
向上转型:用父类类型去创建子类对象
多态的使用前提
- 1 要有继承或实现关系
- 2 要有方法的重写
- 3 要有父类接收子类对象
多态性的体现
抽象类、接口的使用体现了多态性。(抽象类、接口不能实例化)
多态性的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
总结:编译,看左边;运行,看右边
向上转型
同自动类型转换(小 —>大),用父类的引用指向子类的对象
当子类和父类有相同的属性和方法时,在使用向上造型后,方法的调用问题,请记住一句0话:属性看父类,方法看子类,能点出什么看父类
向下转型
1、为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型
2、如何实现向下转型:使用强制类型转换符:()
注意
1、使用强转时,可能出现ClassCastException(类型转换异常):当试图将对象强制转换为不是实例的子类时,抛出该异常;
2、所以只有转换的~~(子类~~)对象是~~(父类)~~类型的子类时,才能强转成功
2、为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行 instanceof 的判断,一旦返回true,就进 行向下转型。如果返回false,不进行向下转型
instanceof的使用:
① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,则 a instanceof B也返回true,其中,类B是类A的父类。
③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误
面向对象 - 下
关键字:this
this关键字可以理解为:当前对象 或 当前正在创建的对象
可以调用的结构:属性、方法;构造器
this调用属性、方法:
1、在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法
但是,通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参
3、在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法
但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参
this调用构造器:
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)“方式调用自己
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)”
④ 规定:"this(形参列表)“必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器
关键字:super
super 关键字可以理解为:父类的
可以调用的结构:属性、方法、构造器
super调用属性、方法:
1、我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是, 通常情况下,我们习惯省略"super."
2 、特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的 方式,表明调用的是父类中声明的属性
3、特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方 法"的方式,表明调用的是父类中被重写的方法
super调用构造器:
① 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
② "super(形参列表)"的使用,必须声明在子类构造器的首行!
③ 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)“只能二一,不能同时出现
④ 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
⑤ 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)”,调用父类中的构造器
关键字:static
可以用来修饰的结构:主要用来修饰类的内部结构属性、方法、代码块、内部类
static修饰属性:静态变量(类变量)
1、属性是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都**独立的**拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,多个对象**共享同一个静态变量**。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
2、static修饰属性的其他说明:
静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
静态变量的加载要早于对象的创建。
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中
类变量 实例变量
类 yes no
对象 yes yes
static修饰方法:静态方法(类方法)
静态方法 vs 非静态方法(实例方法)
- 静态方法:只能调用静态的方法或属性
- 非静态方:,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
static的注意点:
静态方法随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
在静态的方法内,不能使用this关键字、super关键字
关于静态属性和静态方法的使用,都应从生命周期的角度去理解
静态方法 非静态方法
类 yes no
对象 yes yes
static关键字的使用:
关于属性:
属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static
关于方法:
操作静态属性的方法,通常设置为static的
工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
饿汉式:
class Bank{
//1.私化类的构造器
private Bank(){}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
懒汉式(线程安全):
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
if(instance == null){
synchronized (Bank.class) {
if(instance == null){
instance = new Bank();
}
}
}
return instance;
}
}
关键字:final
可以用来修饰:类、方法、变量
final 用来修饰类
表明此类不能被其他类所继承。比如:String类、System类、StringBuffer类等
final 用来修饰方法
表明此方法不可以被重写。比如:Object 类中 getClass()
final 用来修饰变量
表明此时的"变量"就称为是一个常量
注意
- final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化
- final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值
- static final 用来修饰属性:全局常量
关键字:abstract
可以用来修饰:类、方法
abstract不能用来修饰:属性、构造器、私有方法、静态方法、final的方法、final的类
abstractt修饰类:抽象类
1、此类不能实例化
2、抽象类中一定有构造器,便于子类实例化时调用(涉及:多态,子类对象实例化的全过程)
3、开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
abstract修饰方法:抽象方法
抽象方法只方法的声明,没方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中也可以没有抽象方法
若子类重写了父类中的所的抽象方法后,此子类方可实例化
若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
关键字:interface
接口的特点:
1、接口不能创建对象(实例化),而且接口中没有构造方法,必须通过实现类去创建对象
2、接口可以多继承,继承之后,会将所有的抽象方法继承过来
3、一个类可以实现多个接口
接口的定义:
JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的 (可省略不写)
抽象方法:public abstract的 (可省略不写)
JDK8及以上:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
接口的使用:
Java开发中,接口通过让类去实现(implements)的方式来使用.
- 如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
- 如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
- 接口的具体使用,体现多态性
JDK8关于接口的新规范
1、接口中定义的静态方法,只能通过接口来调用。
2、通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
3、如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的 是父类中的同名同参数的方法。–>类优先原则
4、如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。–>接口冲突。这就需要我们必须在实现类中重写此方法5、如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
- super.方法名(); 调用的是父类中声明的
- 接口名.super.方法名();调用接口中的默认方法
抽象类和接口的异同?
-
相同点:不能实例化;都可以包含抽象方法的
-
不同点:
1)把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明声明方法的存在而不去实现它的类被叫做抽象类(abstract class)
接口是抽象类的变体。在接口中,所有方法都是抽象的
2)类:单继承性 接口:多继承
3)类与接口:多实现
代理模式,工厂设计模式
关键字:package / import
关键字package
1.为了更好的实现项目中类的管理,提供包的概念
2.使用package声明类或接口所属的包,声明在源文件的首行
3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
4.每 “.” 一次,就代表一层文件目录
MVC设计模式
关键字import
import:导入
- 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
- 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示
- 使用"xxx.*"方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
- import static:导入指定类或接口中的静态结构:属性或方法。