1.什么是面向对象
OOP,Object Oriented Programming,就是面向对象的编程,一切事物皆对象。将现实的事物抽象出来,抽象成类,通过继承,实现,组合的方式把万事万物都给容纳了。实现了对现实世界的抽象。**面向对象更符合人类的思维,面向过程则是机器的思想**
1. 1. 面向对象三大特点
封装:易维护,隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。<br /> 继承:易扩展,提高代码复用性;继承是多态的前提。<br /> 多态:易复用,**父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。**提高了程序的拓展性。<br /> 面向对象的多态性,即“一个接口,多个方法”。多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方 式。多态性允许一个接口被多个同类使用,弥补了单继承的不足。
1.2. 面向对象三个核心特质
可重用性:代码重复使用,减少代码量,提高开发效率。下面介绍的面向对象的三大核心特性(继承、封装和多态)都围绕这个核心。<br /> 可扩展性:指新的功能可以很容易地加入到系统中来,便于软件的修改。<br /> 可管理性:能够将功能与数据结合,方便管理。
2. 类
2.1. abstract类
如果一个类中没有包含足够的信息来描绘一个具体的对象,那么这样的类称为抽象类。
类被 abstract 修饰,则该类为抽象类,抽象类不能被实例化,,也就是不能使用 new 关键字创建对象但抽象类中可以有抽象方法(使用 abstract 修饰的方法)和具体方法(没有使用 abstract 修饰的方法)。继承该抽象类的所有子类都必须实现该抽象类中的所有抽象方法(除非子类也是抽象类)。final:表示将该成员变量声明为常量,其值无法更改。
2.2. abstract方法
如果一个方法使用 abstract 来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是 abstract 关键字只能用于普通方法,不能用于 static 方法或者构造方法中。
抽象方法的 3 个特征如下:
抽象方法没有方法体
抽象方法必须存在于抽象类中
子类重写父类时,必须重写父类所有的抽象方法
2.3. 抽象类方法
abstract:表示限定该成员方法为抽象方法。抽象方法不提供具体的实现,并且所属类型必须为抽象类。
final:表示限定该成员方法不能被重写或重载
2.3. this的作用
this.属性名:在构造方法中经常使用。
案例省略…
this.方法名:让类中一个方法,访问该类里的另一个方法或实例变量。
//假设定义了一个 Dog 类,这个 Dog 对象的 run( ) 方法需要调用它的 jump( ) 方法,Dog 类的代码如下所示:
public class Dog {
// 定义一个jump()方法
public void jump() {
System.out.println("正在执行jump方法");
}
// 定义一个run()方法,run()方法需要借助jump()方法
public void run() {
Dog d = new Dog();
d.jump();
System.out.println("正在执行 run 方法");
}
}
//这种方法可以实现需求,但是在主函数实例化时要调用run方法的话,内存中会出现两个Dog对象。
this 可以代表任何对象,当 this 出现在某个方法体中时,它所代表的对象是不确定的,但它的类型是确定的,它所代表的只能是当前类的实例。只有当这个方法被调用时,它所代表的对象才被确定下来,谁在调用这个方法,this 就代表谁。所以可以用下面的方法满足需求:
// 定义一个run()方法,run()方法需要借助jump()方法
public void run() {
// 使用this引用调用run()方法的对象
this.jump();
System.out.println("正在执行run方法");
}
从第一种 Dog 类定义来看,在 Dog 对象的 run( ) 方法内重新创建了一个新的 Dog 对象,并调用它的 jump( ) 方法,这意味着一个 Dog 对象的 run( ) 方法需要依赖于另一个 Dog 对象的 jump( ) 方法,这不符合逻辑。
第二种 Dog 类定义是当一个 Dog 对象调用 run( ) 方法时,run( ) 方法需要依赖它自己的 jump( ) 方法,与第一种定义类的方法相比,更符合实际情形。
3. 对象的创建
隐含创建对象:
String strName = “strValue”,其中的“strValue”就是一个 String 对象,由 Java 虚拟机隐含地创建。
— 当 Java 虚拟机加载一个类时,会隐含地创建描述这个类的 Class 实例。类的加载是指把类的 .class 文件中的二进制数据读入内存中,把它存放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。
如果一个对象只需要使用唯一的一次,就可以使用匿名对象,匿名对象还可以作为实际参数传递。
匿名对象只在堆内存中开辟空间,而不存在栈内存的引用。
new Person(“张三”,30)
4. static方法
static修饰的方法或者变量不需要依赖于对象来进行访问
运行时,Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配。
4.1. 静态方法的特点
调用其他:
静态方法不能调用非静态成员,编译会报错。main() 方法是静态的。如果要在 main() 方法中调用本类中的其他方法,则该方法也必须是静态的,否则需要先创建本类的实例对象,然后再通过对象调用成员方法。
被其他调用:
在类的内部,可以在任何方法内直接访问静态变量。
在其他类中,可以通过类名访问该类中的静态变量。
4.2.注意
1.静态方法不需要通过它所属的类的任何实例就可以被调用,因此在静态方法中不能使用 this 关键字,也不能直接访问所属类的实例变量和实例方法,但是可以直接访问所属类的静态变量和静态方法。另外,和 this 关键字一样,super 关键字也与类的特定实例相关,所以在静态方法中也不能使用 super 关键字。
2.避免通过一个类的对象引用访问此类的静态成员,无谓增加编译器解析成本,直接用类名来访问即可。静态变量是类实例变量,可以用类名.静态属性直接访问,new一个对象需要分配内存空间,无端增加花费成本
5.final
final 用在变量的前面表示变量的值不可以改变,此时该变量可以被称为常量。
final 用在方法的前面表示方法不可以被重写(子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写,又称为方法覆盖。这里了解即可,教程后面我们会详细讲解),可以被重载。
final 用在类的前面表示该类不能有子类,即该类不可以被继承。
final 修饰的局部变量必须使用之前被赋值一次才能使用。
final对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
final 修饰的类不能被继承。当子类继承父类时,将可以访问到父类内部数据,并可通过重写父类方法来改变父类方法的实现细节,这可能导致一些不安全的因素。为了保证某个类不可被继承,则可以使用 final 修饰这个类。
6. 继承与多态
Java 的继承通过 extends 关键字来实现,extends 的英文意思是扩展,而不是继承。extends 很好的体现了子类和父类的关系,即子类是对父类的扩展,子类是一种特殊的父类。从这个角度看,使用继承来描述子类和父类的关系是错误的,用扩展更恰当。
子类一般比父类包含更多的属性和方法。可提高代码的可扩展性,更好的实现父类的方法。
6.1. 一些继承特点
1.类的继承不改变类成员的访问权限,也就是说,如果父类的成员是公有的、被保护的或默认的,它的子类仍具有相应的这些特性,并且子类不能获得父类的构造方法。
2.由于子类不能继承父类的构造方法,因此,如果要调用父类的构造方法,可以使用 super 关键字。super 可以用来访问父类的构造方法、普通方法和属性。
3.父类构造方法有参数但未定义,子类必须显式定义构造方法
6.2.缺陷
增强代码耦合性(开发项目的原则为高内聚低耦合)。当父类的常量、变量和方法被修改时,需要考虑子类的修改,有可能会导致大段的代码需要重构。
6.3. 两种引用类型的转型
Java 语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换。Java 中引用类型之间的类型转换(前提是两个类是父子关系)主要有两种,分别是向上转型(upcasting)和向下转型(downcasting)。
1.父类引用指向子类对象为向上转型(向上:狗是动物,不用强转)
fatherClass obj = new sonClass();
其中,fatherClass 是父类名称或接口名称,obj 是创建的对象,sonClass 是子类名称。
向上转型就是把子类对象直接赋给父类引用,不用强制转换。使用向上转型可以调用父类类型中的所有成员,不能调用子类类型中特有成员,最终运行效果看子类的具体实现。
2.子类对象指向父类引用为向下转型(向下:动物是狗,需要强转)
sonClass obj = new fatherClass();
sonClass obj = (sonClass) fatherClass;
向下转型可以调用子类类型中所有的成员,不过需要注意的是如果父类引用对象指向的是子类对象,那么在向下转型的过程中是安全的,也就是编译是不会出错误。但是如果父类引用对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现我们开始提到的 Java 强制类型转换异常,一般使用 instanceof 运算符来避免出此类错误。
6.4. 多态
Java 实现多态有 3 个必要条件:继承、重写和向上转型。只有满足这 3 个条件,开发人员才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而执行不同的行为。
7. 接口
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(Interface)。接口是 Java 中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成。
具有 public 访问控制符的接口,允许任何类使用;没有指定 public 的接口,其访问将局限于所属的包。
7.1. 接口的声明
方法的声明不需要其他修饰符,在接口中声明的方法,将隐式地声明为公有的(public)和抽象的(abstract)。
在 Java 接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为 public、static 和 final,即常量,所以接口中定义的变量必须初始化。
接口没有构造方法,不能被实例化。
7.2. 接口的实现
接口的主要用途就是被实现类实现,一个类可以继承一个父类,并同时实现多个接口,implements 部分必须放在 extends 部分之后。使用 implements 关键字。一个接口不能够实现另一个接口,但它可以继承多个其他接口。子接口可以对父接口的方法和常量进行重写。
一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
7.3. 接口和抽象类
相同点
- 都不能被实例化。
- 接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点
- 接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
- 一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
- 接口强调特定功能的实现,而抽象类强调所属关系。
- 接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。