面向对象
类与对象
构成与标准
-
类是 Java 最基本的组成单位,在类体中声明的内容有:
-
成员变量:
声明时不赋值的话会由系统给默认值,一般以
private
修饰以确保数据安全性; -
成员方法:
同名而不同形参的方法即为方法重载;
-
构造方法:用于传入参数初始化实例(给成员变量赋值)
-
缺省的话系统默认会提供一个
public 类名() { }
的构造方法; -
自己声明书写构造方法时系统就不再自动补上上面那样的无参构造方法了,若需要的话要自己写一下;
-
-
-
标准类的构成:
-
成员变量:
-
使用
private
修饰。实质上,权限控制都是为了防止其他程序员误操作的。
-
-
构造方法:
- 提供一个无参构造方法;
- 提供一个带多个参数的构造方法;
-
成员方法
-
提供每一个成员变量对应的
setXxx()
、getXxx()
; -
提供一个能显示对象信息的
show()
;
-
-
创建对象并为其成员变量赋值的两种方式:
- 无参构造方法创建对象后使用
setXxx()
赋值;- 使用带参构造方法直接创建带有属性值的对象;
内存说明
-
分为栈内存和堆内存:
- 对象的数据都存储在堆内存;
- 方法执行时的方法体内的局部变量都存储在栈内存中;
-
局部变量与成员变量在代码内存中的区别:
- 成员变量存储在堆内存中,可以只声明不赋值,系统会自动给其赋值为
0, false, null
这样的值; - 局部变量存储在栈内存中,方法体执行完后数据被清空,必须赋值后才可使用;
- 成员变量存储在堆内存中,可以只声明不赋值,系统会自动给其赋值为
继承
特性
-
继承的代码功能:
继承可以让子类访问调用父类的属性和方法、也可以重写父类的属性和方法,还可以追加新的属性和方法。
-
继承的特点:
-
它提高了代码的复用性和维护性;
- 复用性:多个类的相同成员可以放到同一个类中。
- 维护性:若代码需要修改,修改一处即可。
-
它也增加了代码的耦合度;
- 耦合度:若父类变动,则子类不得不跟着变动。
-
继承不能滥用,一般只当满足 is a 关系的时候才使用。
-
对象的构造
-
构造顺序:
Java 中,每个对象的构造(
new
),都必须先构造其父类对象,然后再构造本类对象,对于多层继承关系,就是按继承树的结构从上到下的顺序逐级构造对象。如:对于3层继承关系
class A extends Object
,class B extends A
,执行语句new B();
时,会先构造出一个 Object 类的对象,再构造出一个 A 类的对象,最后再构造出一个 B 类的对象,在理解上和代码书写上它们是一个整体的 B 对象,在实际上它们是由继承关系联系着的3个对象。 -
构造方法的执行:
-
对应到代码,构造对象需要执行构造方法。
-
当某个类中未声明构造方法时(即缺省情况),系统会为其提供一个默认的无参构造方法:
public 类名() { }
; -
若自己在类中写了构造方法,系统就不再自动补上上述默认的无参构造方法;
在自己写含参构造方法的时候,最好把无参构造方法也写一下,因为它子类的构造方法在默认情况下会调用它的无参构造方法。
-
-
同上面的对象构造顺序所述,对于构造方法的执行,每一个构造方法中都要先调用其父类的构造方法(每个构造方法的第一条语句必须是调用父类的构造方法),直至
Object
的构造方法。-
当某个构造方法中的未在第一条语句位置调用父类构造方法时(即缺省情况),系统会为其在第一条语句的位置上提供一条调用其父类默认无参构造方法的语句:
super();
; -
若自己在构造方法中的第一条语句位置调用了父类的某个构造方法,系统就不再自动补上调用上述的默认无参构造方法的语句;
- 用
super(参数列表)
就是调用父类对应的含参构造方法。 - 另外,
this()
或this(参数列表)
语句代表调用本类的构造方法(暂时还不知道用在哪里)
- 用
-
-
成员查找顺序
如前所属,每个对象的构造,实际上都会构造其继承树上的多个对象,对于这多个对象的成员变量和方法,在代码中要访问调用它们时,按如下的规则进行:
-
在类 A 的方法中访问或调用类 A 自己及其继承体系的成员:
-
直接书写
xxx
的方式(xxx 为变量名或方法名)访问变量或调用方法:-
变量:
-
方法:
-
-
使用
this
和super
关键字指代本类继承体系中某层对象的引用,用对象引用.xxx
的方式访问成员变量或调用方法时:-
this
指代的是继承层级体系中,本条代码所处的类的实例的引用,用它访问成员变量或调用成员方法,代表从本类的开始向上查找; -
super
指代的是继承层级体系中,本条代码所处的类的父类的实例的引用,用它访问成员变量或调用成员方法,代表从父类的开始向上查找;
关键字 访问成员变量 调用方法 this this.成员变量名
:访问本类对象的成员变量this.成员方法名()
: 用本类对象调用方法super super.成员变量名
:访问父类对象的成员变量super.成员方法名()
:用父类对象调用方法 -
-
-
在类 B 的方法中访问或调用类 A 及其继承体系的成员:
即,在类 B 用
类A对象引用.xxx
的方式访问成员变量或调用方法时-
当引用类型与对象实际类型一致时,成员变量和方法都是从该类本类开始,向上查找。
所谓一致,即如:
A a = new A();
-
当引用类型取的是对象类型的某层父类时,即“多态”的情形下:
所谓使用多态,即如:
class B extends A
,class C extends B
,而使用A c = new C();
、B c = new C();
或A b = new B();
等等。-
成员方法:从实际对象的类型开始向上查找重写了父类方法的方法;
-
成员变量:根据引用的类型开始向上查找,不看实际对象是什么类型;
-
-
多态
多态特性
-
多态:
对于继承或实现关系,存在方法重写情形,用父类引用指向子类对象,再用父类引用调用被子类重写的方法。
-
多态的好处:
定义方法的时候,使用父类作为参数类型,在使用的时候,使用具体的子类型对象参与操作,这样提高了程序的扩展性。
-
引用的转型:
- 向上转型:从子到父,将父类引用转为子类引用;
- 向下转型:从父到子,将父类引用转为子类引用;
不管引用是什么类型,Java 总能知道此引用指向的实际对象的具体类型,所以:
- 能多态调用方法;
- 类型转换的时候报的异常信息也是引用指向的实际对象的具体类型相关的。
-
多态的使用:
- 父类类名作为形参或返回值类型,方法体中用父类引用调方法,实际传入或返回子类对象引用;
- 接口作为形参或返回值类型,方法体中用接口引用调方法,实际传入或返回实现类对象引用;
抽象类
-
语法说明:
抽象类本身不能实例化,需要子类重写它的抽象方法后再实例化使用。
抽象类的定义方式如:
public abstract class 类名 {}
抽象方法为没有方法体的方法,它声明在抽象类中。
抽象方法的定义方式如:
public abstract 返回值类型 方法名();
从语法角度说:
-
类中如果有抽象方法,该类必须定义为抽象类,抽象类中不一定要有抽象方法,但有抽象方法的类一定是抽象类。
-
抽象类不能直接实例化,需要通过子类实例化,再多态方式使用。
所以它有构造方法,但仅可用于子类构造时先构造它使用。
-
抽象类的子类,要么重写抽象类中的所有抽象方法,要么也是抽象类。
-
-
用途:
抽象类可有抽象方法用于限定子类必须完成某些动作,也可有非抽象方法用于提高代码复用性。
接口
这里的整理架构,后期可以整合起来,不分裂成按版本的。但目前还没了解完,所以暂时按版本分裂一下。
-
不同于常规的“类”, Java 中的接口更多的体现在对功能行为本身的抽象。
-
语法:
-
接口声明方式:
public interface 接口名 { ... }
-
类实现接口的声明方式如:
public class 类名 implements 接口名 { ... }
-
-
接口的特点:
- 接口不能实例化,也没有构造方法,需要通过其实现类的对象多态使用。
- 接口可以多继承,类实现接口的时候也可以实现多个接口。
- 接口中的成员会被系统提供修饰符:
- 成员变量默认修饰符是
public static final
。 - 成员方法默认修饰符是
public abstract
(抽象方法)。
- 成员变量默认修饰符是
-
JDK 版本更新的新特性:
- JDK 8 之前,接口中的方法必须是抽象方法,其实现类必须重写该接口内的所有(抽象)方法。
- 后续版本中,接口中可以有除了抽象方法以外的方法,其实现类依然是只需要重写该接口内的所有抽象方法即可,其它类型的方法不强制重写(当然也可以自己去重写)。
-
默认方法:
public default 返回值类型 方法名(参数列表) { 方法体... }
-
默认方法不强制重写,但也可以被重写,重写的时候要去除
default
关键字。 -
默认方法的
public
关键字可以省略不写,default
必须要写。
-
-
静态方法:
public static 返回值类型 方法名(参数列表) { 方法体... }
- 接口的静态方法只能通过接口名调用,不可以通过其实现类的类名或对象调用!
- 静态方法的
public
关键字可以省略不写,static
必须要写。
-
私有方法:
// 私有非静态方法 private 返回值类型 方法名(参数列表) { 方法体... } // 私有静态方法 private static 返回值类型 方法名(参数列表) { 方法体... }
- 私有方法基本上就是为了给默认方法和静态方法调用使用的;
- 默认方法可以调用私有的静态方法和非静态方法;
- 静态方法只能调用私有的静态方法;
内部类
- 种类分为 3 种:成员内部类、局部内部类、匿名内部类。
- 简单思考(不一定对):
- 成员内部类和局部内部类,在早期 JDK 用它们写自己 API (如集合等)实现方式(翻看源码能看出),以实现内部封装。
- 匿名内部类,在早期用于简单的函数式编程。
-
成员内部类: 在一个类的成员位置声明的类,有如下特点:
-
成员内部类可以直接访问其所在的外部类的所有成员。
-
外部类要访问其内部类的成员,需要先创建内部类的对象,然后就可以直接访问该内部类的全部成员。
// 举例如下: public class Outer { private int a = 1; private class Inner { private int b = 2; private void show() { System.out.println(a); } } public void method1() { Inner i = new Inner(); i.show(); // 输出1 } public void method2() { Inner i = new Inner(); System.out.println(i.b); // 输出2 } }
-
对于非私有权限的成员内部类,外界要想访问,则可通过其所在外部类的对象构造它的对象来使用。
显然,
private
权限的成员内部类,外界无法访问。// 举例如下: // 声明: public class Outer { public class Inner { public void show() { ... } } } // 使用: { ... Outer.Inner oi = new Outer().new Inner(); oi.show(); ... }
-
-
局部内部类: 在一个方法体中声明的类,有如下特点:
-
局部内部类可以直接访问其所在外部类的所有成员和所在方法体内的局部变量。
-
局部内部类只能在声明所在的方法体内使用(声明时也不允许写权限修饰符):在方法内部创建对象即可使用。
// 举例如下: public class Animal { private int a = 1; public void method() { class Cat { void show() { System.out.println(a); } } Cat cat = new Cat(); cat.show(); // 输出1 } }
- 匿名内部类: 准确地说,是匿名内部对象。
-
使用匿名内部类的前提是存在一个类(具体类/抽象类都行)或者接口。
// 在方法体中使用如下的语法格式来构造出一个匿名内部类对象: new 类名或者接口名() { 重写的方法; };
-
匿名内部类最常用的用途——函数式编程:
它常常用于一次性的使用,即不用专门写一个类的情况下,作为方法参数传入或方法返回值返回(多态)。
// 如: public interface Jumpping{ void jump(); } public class JumppingOperator { public void mehtod(Jumpping jp) { jp.jump(); } } public class Main { public static void main(String[] args) { JumppingOperator jo = new JumppingOperator(); jo.method(new Jumpping() { @Override public void jump() { System.out.println("jump~"); } }); } }