4. Java面向对象

Java面向对象的三条主线:

  1. Java类及类成员:属性、方法、构造器、代码块、内部类
  2. 面向对象的三大特征:封装性、继承性、多态性、(抽象性)
  3. 其他关键字: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. 创建类,设计类的成员
  2. 创建类的对象
  3. 通过“对象.属性”或“对象.方法”调用对象的结构

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。若有父类方法与接口中的默认方法同名同参数,且子类没有重写该方法,则调用时使用父类方法(类优先原则)。

猜你喜欢

转载自blog.csdn.net/qq_43221336/article/details/107252111