面向对象
概况:
- 抽象
- 类和对象
- 封装:private 降低使用者的成本。
- 继承:代码写起来更简单,方便代码重用。
- 组合:代码写起来更简单,方便代码重用。
- 多态:是封装的更进一步
- 自省(反射):对象在运行时获取到自身的一些信息
面向对象的三大特性:封装、继承、多态
包
通俗讲,就是将很多类放在一起,类似打包,所以可以组成多种多样的包,而且一个类也可能在多个包中。用的时候一般是把具有一定关联关系的类放到同一个包中。包还有的作用是把同名的类放在不同包中,包就起到了命名空间的作用,就可以用包来区分它们。
导入包的方式
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
//也可以用import
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
//使用 import static 可以导入包中的静态的方法和字段
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// 静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
将类放到包中
基本规则:
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中
- 包名需要尽量指定成唯一的名字
- 包名要和代码路径相匹配。例如创建 A.B.C 的包,那么会存在一个对应的路径 A/B/C 来存储代码
- 如果一个类没有 package 语句, 则该类被放到一个默认包中
访问权限:
如果某个成员不包含 public 和 private 关键字, 此时这个成员可以在包内部的其他类使用, 但是不能在包外部的类使用
常见的系统包:
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入
- java.lang.reflflect:java 反射编程包
- java.net:进行网络编程开发包
- java.sql:进行数据库开发的支持包
- java.util:是java提供的工具程序包(集合类等) 非常重要
- java.io:I/O编程开发包
继承
被继承的类称为 父类、基类 或 超类, 对于继承的类称为 子类 、派生类,子类会继承父类的字段,达到代码重用的效果。
规则:
class 子类 extends 父类 {
}
- 使用 extends 指定父类
- Java 中一个子类只能继承一个父类 (C++/Python等语言支持多继承)
- 子类会继承父类的所有 public 的字段和方法
- 对于父类的 private 的字段和方法, 子类中无法访问
- 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用
如果把字段设为 private,子类不能访问,但是设成 public, 又违背了 “封装” 的初衷,两全其美的办法就是 protected 关键字。
对于类的调用者来说, protected 修饰的字段和方法是不能访问的
对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的
Java 中对于字段和方法共有四种访问权限:
- private: 类内部能访问,类外部不能访问
- 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问
- protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问
- public 类的内部和类的调用者都可以使用
多层继承一般不超过三层为好,否则代码可读性就太差。
final 关键字也能修饰类 final public class …
此时表示被修饰的类就不能被继承,比如我们平时用的String。
组合并没有涉及到特殊的语法(诸如 extends 这样的关键字),仅仅是将一个类的实例作为另外一个类的字段,这是设计类的一种常用方式之一。举例:
public class Student {
...
}
public class Teacher {
...
}
public class School {
public Student[] students;
public Teacher[] teachers;
}
多态
向上转型
sofa1 是一个父类 (Furniture) 的引用, 指向一个子类 (Sofa) 的实例. 这种写法称为 向上转型
Furniture sofa1 = new Sofa("伊客多兰");
向上转型发生的情况:
- 直接赋值(如上)
- 在方法中作为参数(传参)
- 方法的返回值
//在方法中作为参数(传参)
public static void main(String[] args) {
Sofa sofa = new Sofa("伊客多兰");
wash(sofa);
}
public static void wash(Furniture furniture) {
furniture.wash();
}
}
// 执行结果 伊客兰多的沙发正在清洗
//方法的返回值
//此时方法 findMyAnimal 返回的是一个 Animal 类型的引用, 但是实际上对应到 Bird 的实例
public class Test {
public static void main(String[] args) {
Animal animal = findMyAnimal();
}
public static Animal findMyAnimal() {
Bird bird = new Bird("小鸟");
return bird;
}
}
动态绑定
在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时才决定的(编译期无法决定), 称为动态绑定。
重写
子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override)。
- 重写和重载完全不同
- 普通方法可以重写, static 修饰的静态方法不能重写
- 重写中子类的方法的访问权限不能低于父类的方法访问权限
多态的理解
一个引用,能表现出多种不同形态。多态能让类的使用者不必知道这个类的类型,只需要知道这个对象具有某个方法,调用该方法就可以达到使用目的。
抽象类
抽象类存在就是为了被继承.抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
abstract class 类名 {
abstract public void 方法名();
}
在方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体,即没有 { }, 不能执行具体代码。
抽象类中可以包含其他的非抽象方法, 也可以包含字段,这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用。
接口
接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量(
public static final)。
- 使用 interface 定义一个接口
- 接口中的方法一定是抽象方法, 因此可以省略 abstract
- 接口中的方法一定是 public, 因此可以省略 public
- 使用 implements 继承接口,不是extends
- 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例
- 接口不能单独被实例化.
Java 中只支持单继承, 一个类只能 extends一个父类. 但是可以同时实现多个接口,且接口间可以相互继承(这里关键字用 extends)