Java面向对象的三条主线:
- Java类及类成员:属性、方法、构造器、代码块、内部类
- 面向对象的三大特征:封装性、继承性、多态性、(抽象性)
- 其他关键字:this、super、static、final、abstract、interface、package、import
面向过程(POP):强调的是功能行为
面向对象(OOP):将功能封装进对象,强调具备了功能的对象
1. 类和对象
类是对一类事物的描述,是抽象的、概念上的定义。对象是实际存在的该类事物的每个个体,也称为实例(instance)。面向对象的程序设计重点在于类的设计,设计类就是设计类的成员。
属性和方法是类的主要成员,以下名称描述的概念等同:
- 属性 = 成员变量 = Field
- 方法 = 函数 = Method
1.1 类的属性
属性(成员变量)与局部变量的异同:
1.1.1 相同点
- 定义变量的格式相同:数据类型 变量名 = 变量值
- 要先声明,后使用
- 都有其对应的作用域
1.1.2 不同点
- 在类中声明的位置不同:属性声明在类里、方法外。局部变量声明在方法内、方法的形参部分、代码块内。
- 权限修饰符的不同:属性可以在声明时使用权限修饰符(public、private、protected、缺省)。局部变量没有修饰符(与其所在方法的权限修饰符相同)。
- 默认初始化值不同:属性根据其类型有默认的初始值。局部变量一定要显示地赋值(形参可以在调用时赋值)。
- 在内存中加载的位置不同:属性加载到堆空间中(非static的)。局部变量加载到栈空间中。
成员变量又分为:实例变量(不以static修饰)、类变量(以static修饰)
局部变量又分为:形参、方法局部变量、代码块局部变量
1.2 类的方法
1.2.1 方法的声明:权限修饰符 返回值类型 方法名(形参列表){ 方法体 }
static、final、abstract 也可以用来修饰方法
1.2.2 方法的使用
方法中可以直接调用当前类的属性或方法。方法中不可以再定义方法、
1.2.3 方法的重载(overload)
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数换或参数类型不同即可。在调用时,根据方法参数列表的不同来区分类别,当参数类型有完全对应的方法时使用该方法,若没有完全对应类型的方法则会自动类型提升。方法是否重载与方法的返回值类型无关。
1.2.4 可变个数的形参的方法
//以下两种方法一致,不能共存
public static void test(int a ,String[] books); //JDK5.0以前
public static void test(int a, String ... books); //JDK5.0以后,books也是一个数组
- 传入多个同一类型的变量,调用时形参个数从0到 ∞ \infty ∞
- 可变个数的形参的方法与同名的方法之间构成重载
- 可变个数形参在方法中一定要声明在所有形参的最后
- 一个方法中最多只能有一个可变个数的形参
1.2.5 方法参数的值传递机制
变量在赋值的过程中,若变量是基本数据类型则所赋的值是变量保存的数据,若变量是引用数据类型则所赋的值是变量保存的数据的地址值。
形参也属于变量,将实参传给形参的过程也满足以上变量赋值机制。
1.3 对象
1.3.1 类和对象的使用
- 创建类,设计类的成员
- 创建类的对象
- 通过“对象.属性”或“对象.方法”调用对象的结构
1.3.2 类与对象的关系:如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的)。
1.3.3 对象的内存解析:
引用数据类型的变量在栈空间中存储的不仅仅是地址值,还保存了具体的数据类型,所以不同类型之间的变量不能相互赋值。
1.3.4 匿名对象
创建对象时没有显示地赋予名字,即为匿名对象。匿名对象只能调用一次。
1.4 构造器
构造器 = 构造方法 = constructor
1.4.1 构造器的作用:创建对象、初始化对象的属性
1.4.2 定义构造器的格式:权限修饰符 类名(形参列表){ }
- 如果没有显示地定义构造器,系统会提供一个默认的空参构造器。
- 一个类中定义的多个构造器,彼此构成重载。
- 一旦显示地定义了类的构造器之后,就不再提供默认的空参构造器了。
- 若构造器被private修饰,则只能在本类中创建对象,外部类只能调用已创建好的对象。
1.5 代码块
代码块只能使用static修饰,由此分为静态代码块和非静态代码块。
1.5.1 非静态代码块
- 内部可以有输出语句,随着对象的创建而执行,多个非静态代码块按顺序执行
- 可以在创建对象时,对对象的属性进行初始化,非静态代码块的执行要早于构造器
1.5.2 静态代码块
- 内部可以有输出语句,随着类的加载而执行,多个静态代码块按顺序执行
- 可用于初始化类的信息
- 静态代码块内部不能调用非静态的结构
1.6 内部类
1.6.1 定义
当一个事物的内部,还有一个部分需要完整的结构进行描述(不能直接用属性或方法定义),而这个内部的完整结构又只为外部事物提供服务(定义一个外部类浪费资源),那么整个内部的完整结构最好使用内部类。
在Java中,允许一个类定义于另一个类的内部,前者称为内部类,后者称为外部类。
1.6.2 分类
内部类分为:成员内部类(直接定义在外部类中)&局部内部类(和局部变量位置相同)。成员内部类又分为静态与非静态。
- 成员内部类中可以:① 定义属性、方法构造器;② 可以被static、final、abstract修饰;③ 调用外部类的结构;④ 可以使用4种权限修饰符
1.6.3 使用
- 内部类一般用在定义它的类或语句块之外,在外部引用它时必须给出完整的名称。
- 内部类的名字不能与其外部类类名相同。
- 静态成员内部类直接通过外部类调用构造器:
Person.Dog dog = new Person.Dog();
- 非静态成员内部类要创建外部类对象之后,再调用内部类构造器:
Person p = new Person();
Person.Bird bird = p.new Bird();
- 如何区分外部类和内部类的变量:
this.变量名 //调用内部类属性
外部类名.this.变量名 //调用外部类属性
- 局部内部类的使用:常使用一个方法,返回某个接口的实现类的对象。
2. 面向对象的特征
2.1 封装和隐藏
2.1.1 问题的引入
当创建了类的对象以后,如果直接通过“对象.属性”的方式对相应的对象属性赋值的话,可能会出现不满足实际情况的条件。于是考虑不让对象直接作用于属性,而是通过“对象.方法”的形式来控制对象对属性的访问,在该方法中添加对属性的限制条件。为避免用户直接对属性进行访问,将属性声明为私有的(private),此时对属性就体现了封装性。
2.1.2 封装性的体现
属性的封装性体现:将类的属性xxx私有化(private),同时,提供公共的(public) 方法来获取(getXxx)和设置(setXxx)此属性的值。
public class AnimalTest {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
除了将属性私有化以外,封装性还体现为:不对外暴露的私有化方法、单例模式中对构造器的私有化
2.1.3 权限修饰符
- 权限修饰符从小到大排列:private、缺省、protected、public
- 四种权限修饰符可以用来修饰:类、类的内部结构(属性、方法、构造器、内部类)
- 权限修饰符定义在类成员前时,对象对该类成员的访问权限如下:
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
- 类和接口只能用public和缺省修饰
2.1.4 JavaBean
JavaBean是一种Java语言写成的可重用组件,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共构造器(反射要用)
- 有属性,且有对应的get、set方法
用户可以使用JavaBean将任何可以用Java代码创造的对象进行打包,其他的开发者可以通过各种方式来使用这些对象。JavaBean提供了一种随时随地地复制和粘贴的功能,而不用关心任何改变。
2.1.5 UML类图
- +表示public类型,-表示private类型,#表示protected类型
- 方法的写法:方法类型【+、-】 方法名(参数名:参数类型):返回值类型
2.2 继承
2.2.1 继承的使用:class A extends B{}
A:子类 = 派生类 = subclass
B:父类 = 超类 = 基类 = superclass
一旦子类A继承父类B以后,A就获取了B中声明的结构、属性、方法。子类继承父类以后,还可以声明自己特有的属性和方法。
2.2.2 继承性特点
- 减少了代码的冗余,提高了代码的复用性
- 有关系的类之间继承,不要仅为了获取其他类中的某个功能而去继承
- 子类继承了父类的方法和属性,子类是对父类功能的扩展
- 子类不能直接访问父类中私有的成员变量和方法,但可通过公共方法访问
- Java只支持单继承,一个子类只能有一个父类,一个父类可以有多个子类。允许多层继承。
2.2.3 Object类
- 如果没有显示地声明一个类的父类,则该类继承于java.lang.Object类
- 所有的java类都直接或间接地继承于Object类
2.2.4 方法的重写(override/overwrite)
- 在子类中可以根据需要从父类中继承来的方法进行改造,在程序执行时,子类方法将覆盖父类方法。
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
- 子类重写方法的返回值类型不能大于父类被重写方法的返回值类型(必须是原返回值类型本身或其子类),void方法重写的返回值类型只能是void,基本数据类型方法重写的返回值必须是相同的基本数据类型。
- 子类重写方法使用的访问权限不能小于父类被重写方法的访问权限,private权限方法无法被重写。
- 子类方法抛出的异常不能大于父类被重写方法的异常。
- 子类与父类中同名参数的方法必须同时为static(重写),或同时为非static(非重写)。
2.2.5 super关键字
- 可以在子类的方法或构造器中,通过“super.属性”或“super.方法”的方式,显式地调用父类中声明的属性或方法。
- 在子类构造器内部,可以使用“super(形参列表)”的方式,调用父类中指定的构造器,且该调用必须声明在当前构造器的首行(和this二选一)。
- 在构造器首行,若没有显式地声明“this(形参列表)”或“super(形参列表)”,则默认调用父类中的空参构造器。
2.3 多态
2.3.1 什么是多态性
Java中的多态性体现为对象的多态性,即父类的引用指向子类的对象,可以直接应用在抽象类和接口上。
Java引用变量时有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。若编译时类型和运行时类型不一致,就出现了对象的多态性。
2.3.2 多态性的使用
- 格式:父类 变量 = new 子类()
- 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法。
- 多态的使用前提:① 有类的继承关系;② 有方法的重写
- 多态性只适用于方法,不适用于属性
- 多态性创建的对象只能调用父类中有的方法,因为在编译期无法确定要创建哪种对象。若想该对象使用子类中有而父类中没有的方法,则需要向下转型。
- instanceof操作符:x instance of A检验x是否为类A的对象,返回值为boolean型。如果x属于类A的子类B,也返回true。
3. 关键字
3.1 this
this表示当前对象,可以调用类的属性、方法、构造器。在方法内部可以使用this来区分局部变量和属性。
- this在方法内部使用,表示这个方法所属对象的引用
- this在构造器内部使用,表示这个构造器正在初始化的对象
- 在构造器内部,可以使用“this(形参列表)”的方式,调用本类中的其他构造器,且该调用必须声明在当前构造器的首行。
public Person(){
System.out.println("初始化");
}
public Person(String name){
this();
this.name = name;
}
3.2 package、import
3.2.1 package(包)
- 为了更好地实现项目中类的管理,提供包的概念
- 使用package声明类或接口所属的包,声明在源文件的首行
- 每一个“ . ”代表一层文件目录
3.2.2 MVC设计模式
MVC是常用的设计模式之一,将整个程序分为3个层次:视图模型层、控制器层、数据模型层。这种将程序输入输出、数据处理、数据展示分离开来的设计模式,使程序结构变得灵活且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
- 模型层(model):主要处理数据。会定义的包有:model.(bean、dao、db)
- 视图层(view):显示数据。会定义的包有:view.(utils、ui)
- 控制层(controller):处理业务逻辑:会定义的包有:controller.(activity、fragment、adapter、service、base)
3.2.3 import
- 在源文件中显示地用import导入指定包下的类和接口
- 声明在包的声明和类的声明之间
- 可以使用“ xxx.* ”的方式,导入xxx包下的所有类和接口,xxx子包下的结构并不会导入
- 定义在java.lang包下的类或接口,则可以省略import
- 使用本包下的类或接口,也可以省略import
- 如果要使用不同包下同名的类,只能导入其中一个,其他类需要以全类名的方式使用
- import static:导入指定类或接口中的静态结构
import static java.lang.Math.*; //导入static时不能以类结尾,必须以类中的静态结构结尾
3.3 static
static可以用来修饰:属性、方法、代码块、内部类
3.3.1 static修饰属性
将属性按是否被static修饰,可分为:静态变量(静态属性)& 实例变量(非静态属性)
- 实例变量:类的每个对象都独立拥有一套类中的非静态属性。
- 静态变量:也叫类变量,类的所有对象都共享同一个静态属性。类变量随着类的加载而加载,要早于对象的创建,可直接通过类.类变量的形式调用。由于类只会加载一次,类变量在内存中也只会存在一份,类变量存在于方法区的静态域中。
3.3.2 static修饰方法:称为静态方法,叫称类方法,与类变量类似
静态方法中只能调用静态的方法或属性。静态方法中不能使用this或super关键字。
操作静态属性的方法、工具类中的方法,通常设置为static。
3.3.3 main方法
public static void main(String[] args){
}
- main方法是程序的入口
- main方法也是一个普通的静态方法
- main方法可以与控制台交互
3.4 final
final可以用来修饰类、方法、变量
- final标记的类不能被继承,提高了安全性和程序的可读性。如String类、System类、StringBuffer类。
- final标记的方法不能被子类重写。如Object类中的getClass()。
- final标记的变量称为常量,名称大写,且只能被赋值一次。
final修饰属性时,可以赋值的位置有:显示初始化、代码块中初始化、构造器中初始化。
final修饰局部变量时,一旦赋值后就不能进行修改(final的形参传入后也不能修改)。
final修饰的变量是引用数据类型时,变量本身不可变,但其属性可以改变。
static final修饰属性时,称为全局常量。
3.5 abstract(抽象类与抽象方法)
abstract可以用来修饰类和方法
3.5.1 抽象类
- 被abstract修饰的类不可实例化,其子类可以实例化。
- 抽象类中一定有构造器,在子类实例化时使用。
3.5.2 抽象方法
- 抽象方法只有方法的声明,没有方法体,在小括号后直接加分号。
- 包含抽象方法的类一定是一个抽象类。抽象类中不一定有抽象方法。
- 若子类非抽象类,必须重写父类中所有的抽象方法。
- abstract不能修饰私有方法private、静态方法static、final的方法、final的类
3.5.3 抽象类的匿名子类
//Person为抽象类,创建其匿名子类的对象
Person p = new Person(){
@Override
public void eat(){
}
}
3.6 interface(接口)
3.6.1 接口的定义
Java中,接口和类是并列的两个结构。接口定义了一种功能,实现接口的类要实现接口中的功能。接口的本质是契约、标准、规范,定义好之后类都要遵守。
3.6.2 接口的使用
- 在JDK7以前,在接口中只能定义public的全局常量(public static final)和抽象方法(public abstract),这两种前缀在接口中可以省略。
- 在JDK8之后,接口中还能定义静态方法(public static)和默认方法(public default),这两种方法的public可以省略。
- 接口中不能定义构造器,不可实例化。
- 类通过implements实现接口,一个类可以实现多个接口,一个可实例化的类(非abstract)必须实现接口中的所有抽象方法。
class AA extends BB implements CC,DD,EE
- 接口与接口之间也是继承关系,而且可以多继承。
- 接口也可以创建匿名实现类,创建方式与抽象类的匿名子类相同。
- 接口中定义的静态方法只能通过接口来调用,其实现类与实现类的对象无法调用。
以前在Collection接口中定义集合类的功能,在Collections工具类中定义集合可用的方法,Java8以后逐渐可以将两者统一。
- 通过实现类的对象可以调用接口中的默认方法(类似于父类中的方法),且类可以重写该默认方法(一般重写默认方法),重写的方法不加default。若有父类方法与接口中的默认方法同名同参数,且子类没有重写该方法,则调用时使用父类方法(类优先原则)。