文章目录
类与对象
一、基本概念
1. 面向对象简介
Java 是完全面向对象的的编程语言。面向对象编程的组织方式围绕“对象”展开,而不是围绕“行为(方法)”。面向对象所采用的观点是“万物皆对象”。
面向过程:最小的程序设计单元是函数,通过定义各个函数组成。
面向对象:最小的程序设计单元是类,通过定义各个类组成,这些类可生成多个对象,对象对应着具体的某些事物。
2. 类与对象
- 将具有相同属性和行为(方法)的一组对象的集合称之为类。
- 而一个类的实现实例称作一个对象。
- 一个类可以有多个实现对象,即类是一个抽象的概念,对象则是类的具体示例。例如,“人”是一个类,“张三”则是一个实例对象。
3. 面向对象特征
- 封装
封装就是将对象的属性和方法结合并隐藏在对象内部,尽可能隐蔽对象的内部细节,对外形成一个边界,只保留有限的对外接口(特定方法)使之与外界发生联系。好处:使得外界无法随意存取对象内部属性,有效避免外部错误产生的影响,提高系统安全性,使软件错误局部化,减少查错和排错难度。
- 继承
继承通过吸收现有类的数据和方法,并增加新功能或修改现有功能来构建新类。子类拥有父类的全部属性和方法。好处:继承可节省程序开发时间,提高代码正确性。
Java 中只能实现单继承,若想模仿多继承需要借助接口 interface,一个类可以 implements (实现) 多个接口。 - 多态
多态是指子类继承父类以后,对父类的属性或方法可以具有不同的表现行为。好处:降低了类和程序模块间耦合性,提高了类模块的封闭性。
Java 中可以通过子类对父类方法的重写,或者子类中定义同名不同参数列表方法重载来实现多态。 - 抽象
忽略一个主题中与当前目标无关的方面,并不打算了解全部细节,而只是选择其中一部分。万物皆对象,世间几乎所有的事物我们都可抽象为一个类,用对象表达实体的属性和操作。
4. 多态介绍
-
编译与运行
编译:是一种动作,指的是将源代码翻译成目标代码 。将 Java 源文件翻译成字节码文件。
编译时:是一种状态,一个时间段内。
运行:代码要想跑(运行)起来,需要代码加载进内存中。
运行时:将代码加载进内存,使其运行起来,也是一种状态,一个时间段内。 -
分类
编译时多态:编译时就已经确定状态了。也叫静态动态。例如,方法重载,本类对象的创建。
运行时多态:我们常说的多态指的是运行时的多态。核心:编译时不能确定,运行时才能确定状态。一定是有继承关系
存在时才产生。 -
实现条件
必须满足下面三个条件:
要有继承关系,有子类和父类
父类引用指向子类对象
子类要重写父类的方法 -
状态
一个引用变量到底指向的是什么类型?
Animal、Dog、Pig。 Dog 和 Pig 继承自 Animal
Animal animal1 = new Pig(); 编译阶段,计算机无法识别 animal1 指向的是 Animal 类型还是 Pig 类型,必须等到运行时才能确定
Dog dog = new Dog(); 计算机在编译阶段就能确定 dog 指向的具体类型为 Dog
二、 类的声明与对象的创建
1. 定义类
通过关键字 class 来定义一个类。类名首字母必须大写。
Java中使用驼峰命名法,类名每个单词的首字母要大写,例如,AddAge。
方法名每个单词的首字母小写,例如,addAge。
类定义格式如下:
访问符 修饰符 class 类名 {
[ 属性 ]
[ 方法 ]
}
public class Animal{
private char name;
private double weigh;
public double setName(char name){
this.name = name;
}
public double setWeigh(double weigh){
this.weigh = weigh;
}
public void call( ){
System.out.println("call");
}
}
2. 类的实例化:创建对象
- 声明该类类型的一个变量,即定义一个该类的对象的引用
- 创建该对象的实际物理赋值,即在内存中为该对象分配地址空间
Animal a; // 声明对象名
a = new Animal(); // 创建对象,分配内存空间
Animal b = new Animal(); // 一步到位
null 用来标识一个不确定的对象
或者给已经创建过的对象释放内存
。
注意:null 仅可以赋值给引用类型变量,不可赋值给基本类型变量。基本数据类型:整形(byte、short、int、long)、浮点型(float、double)、字符型(char)、布尔型(boolean)。引用数据类型:类(class)、接口(interface)、数组、枚举(enum)、注解(Annotation)。
Animal a = null // 释放空间
3. 初始化对象的过程
Animal a = new Animal(168,98);
- 首先,执行
Animal a;
时,系统为引用类型变量 a 分配堆内存空间,以便后来存储对象实际空间的地址值(即对象引用)。 - 然后,执行
a = new Animal();
时,会创建一个 Animal 对象,为新对象在 栈内存中开辟内存空间来存储对象所有属性并对其默认初始化。(即对象) - 然后,执行构造函数对其继续初始化。
- 最后,执行表达式中的
=
,将所创建对象的内存空间的首地址赋给变量 a ,完成引用的指向操作。
对象引用存放在栈内存中,对象存放在堆内存中。
对象引用存的就是一个地址,指向的是一个内存空间,内存空间存放的是对象的内存地址,根据这个内存地址就可以找到这个对象。
而对象存储的是实际内容
三、 变量与方法
1. 类中变量
- 【类的成员变量】类的属性成员,即在方法成员外部定义的变量
实例变量:没有被 static 修饰,由于没有被 static 修饰,属于类的一个实例对象,只有对象被创建后才有,随着这个实例对象的产生而产生,消亡而消亡。
static 变量:被 static 修饰后属于静态变量,它属于整个类,随着这个类的产生而产生,随着这个类的消亡而消亡,通过 类名. 的方式访问,会随着类的加载而进行初始化,如果没有初始化就默认初始化。 - 【方法内的局部变量】
成员变量:仅在方法内部起作用的局部变量
2. this 、 static 与 final 关键字
-
this 关键字
代表当前所在类的对象,即本类对象。this
在非静态内容
中都可以出现,用于获取当前对象的引用。
当方法的参数或者方法中的局部变量与类的属性同名时,类的属性会屏蔽,此时访问类的属性就需要使用this.属性名
,当然,没有同名情况下可以直接使用属性的名字,不需要 this。 -
static 关键字
修饰静态对象或方法,静态对象或方法从属于类,在内存中只有一份,只能通过类名调用,为该类的所有对象所共有。this 代表本类对象,因此不能用 this 访问静态变量。 -
final 关键字
修饰变量:表示此变量不可修改,即常量。final double PI = 3.14159;
final Teacher t = new Teacher();
修饰方法:表示此方法不可被重写。public final void func(){}
修饰类:表示此类不可被继承。public final class Base{}
3. 构造方法
-
构造方法在对象实例化的时候做初始化赋值
构造方法名要与类名相同,权限修饰符为 public。没有返回值类型。// 以上述 Animal 类为例 public class Animal{ private char name; private double weigh; // 构造函数 public Animal(char name, double weigh){ this.name = name; this.weigh = weigh; } public void setWeigh(double weigh){ this.weigh = weigh; } public double getWeigh(){ return this.weigh; } public void call( ){ System.out.println("call"); } }
-
若类中没有构造方法,编译器会自动加一个不带参数的缺省构造方法,可以执行:
Animal a = new Animal();
一旦自己定义了,缺省的构造方法将不存在,上面的无参初始化将无法执行,想能执行的化可以写在类中。
// 构造函数 1 无参 public Animal(){ } // 构造函数 2 有参 public Animal(char name, double weigh){ this.name = name; this.weigh = weigh; }
4. 类的一般方法
-
语法格式如下
[ 访问符 ] [ 修饰符 ] < 返回类型 > 方法名 [ 参数列表 ] { // 方法体 }
public void setWeigh(double weigh){ this.weigh = weigh; } public double getWeigh(){ return this.weigh; } public void call( ){ System.out.println("call"); }
5. 方法的调用
通过对象引用来调用:如果一个方法没有被 static 修饰那么这个方法是属于类的实例,通过 对象名.方法名
来调用。
特别的,静态方法(static修饰),属于类,通过类名.方法名
调用。
public class Animal{
private char name;
private double weigh;
// 非静态方法
public void call( ){
System.out.println("call");
}
// 静态方法
public static void call2( ){
System.out.println("static");
}
public static void main(String[] args) throws Exception{
Animal a = new Animal();
a.call(); // 对象名.方法名
Animal.call2(); // 类名.方法名
}
}
四、 方法的重载与重写
1. 方法重载
-
在同一个类中,多个方法的方法名相同,参数列表(参数个数、类型、顺序)不同,称为为方法的重载。即
方法名相同、参数列表不同
。注意:返回值不作为方法签名。
public class Summation{ public int add(int a, int b){ return a + b; } public float add(float a, float b){ return a + b; } public double add(double a, double b){ return a + b; } public static void main(String[] args) { Summation a = new Summation(); System.out.println(a.add(1,2)); System.out.println(a.add(1.0F,2.0F)); System.out.println(a.add(1.0, 2.0)); } }
2. 方法重写
方法的重写发生在继承关系中,子类继承父类后,对父类的方法重写使之具有不同的表现行为。重写的方法的签名及返回值类型都与父类中的相同,方法体不同。
注意:
-
方法签名(方法名和参数列表)必须完全相同。
-
返回值类型以及声明的异常可以与父类相同。
-
父类的私有方法不可被重写。否则只会是在子类中定义了新方法。
-
子类的可访问性只能大于等于其父类。(相同或是更公开)
-
静态方法不可重写。
public class Animal{
public void call( ){
System.out.println("call");
}
}
public class Dog extends Animal{
public void call( ){
System.out.println("wang wang !");
}
}
public class Cat extends Animal{
public void call( ){
System.out.println("miao miao~");
}
}
五、访问修饰符权限
访问控制 | private 成员 | 缺省成员 | protected成员 | public 成员 |
---|---|---|---|---|
同类 | √ | √ | √ | √ |
同包 | × | √ | √ | √ |
不同包子类 | × | × | √ | √ |
不同包非子类 | × | × | × | √ |
private 、protected、public 均是关键字,而 friendly 不是关键字,只是一种缺省访问修饰符的称谓。
六、类之间的关系
1. 继承
-
子类继承父类的属性与方法,并且增加 / 重写属性或方法来满足子类需求。使用 extends 关键字来体现继承关系。继承关系示例图如下:
Java 中一个类可以有多个子类,但是一个类不能继承多个父类。只能一对多,不能多对一。即无法实现多继承。Java 中可以通过接口Interface 模仿多继承的实现。public class Person{ private String name; private int age; private Srting gender; // 省略方法 } public class Teacher extends Person{ private float salary; // 薪酬 private String department; // 部门 // 省略方法 } public class Student extends Person{ private int[] score; // 成绩 private String grade; // 年级 // 省略方法 }
-
继承的重写体现了多态:指子类继承父类以后,对父类的属性或方法改写,可以具有不同的表现行为。
方法重写时需注意:
方法签名(方法名和参数列表)必须完全相同。
返回值类型以及声明的异常可以与父类相同。
父类的私有方法不可被重写。否则只会是在子类中定义了新方法。
子类的可访问性只能大于等于其父类。(相同或是更公开)
静态方法不可重写。public class Animal{ public void call( ){ System.out.println("call"); } } public class Dog extends Animal{ public void call( ){ System.out.println("wang wang !"); } } public class Cat extends Animal{ public void call( ){ System.out.println("miao miao~"); } }
-
super 关键字
代表父类对象,作用有:调用父类的构造方法,访问父类的属性和方法调用父类的构造方法:
public class Person{ private String name; private int age; // 2. 因此若想有参无参构造方法都用,需要自行添加 public Person(){ } // 1. 有参构造方法将会覆盖默认的缺省的无参构造方法 public Person(String name, int age){ this.name = name; this.age = age; } }
public class Teacher extends Person{ private float salary; private String department; public Teacher(){ } public Teacher(String name, int age, float salary, String department){ super(name, age); // 调用父类构造方法 this.salary = salary; this.department = department; } }
若子类构造方法中没有明确调用父类的构造方法,系统会自动调用父类的无参构造方法。若此时系统没有无参构造方法会报错。
访问父类的属性和方法 :
public class Person{ private String name; private int age; // 省略构造函数 public void printInfo(){ System.out.println(name,age); // 父类方法 } }
public class Teacher extends Person{ private float salary; private String department; // 省略构造函数 public void printInfo(){ super.printInfo(); // 调用父类方法 System.out.println(salary); } }
-
Object 类
Object 类是所有类的顶级父类,在 Java 体系中,所有类都直接或间接的继承了 Object 类,此类下常用的方法有:方法名 功能说明 public boolean equals
(Object obj)比较两个对象是否相等【可重写】 public final Class getClass
()判断当前对象所属类,返回 class 对象 public String toString
()将当前对象转换为字符串【可重写】 protected Object clone
()生成当前对象的一个备份,并返回副本 public int hashCode
()返回当前对象的散列码 equals() 方法:
使用==
可以比较两个基本类型变量值是否相等。
但是比较两个引用类型的变量是否相等有两种方式:==
表示两个变量是否引用同一个对象,即是否存的是同样的地址值。而equals()
方法比较的是两个指向的对象的内容是否相同。可以重写。toString() 方法:
可以在类内对其进行重写,用来输出对象本身的属性。
public String toString(){ return getClass().getName() + ‘’[name:“+name+”, age:“+age+”]"};
输出 Person [ name: Tom, age: 18 ];
-
2. 实现
类似继承,Java 中可以通过一个类实现多个接口 Interface, 模仿多继承的实现。详细内容见下一章。
3. 依赖
一个类的方法
中操作另一个类的对象,称其依赖
于第二个类
public class Person{
void travel(Car car){ // 方法中操作
car.run("北京");
}
}
class Car{
void run(String city){
System.out.println("汽车开到"+city);
}
}
4. 关联
一个类中使用另一个类的对象作为属性
。
public class Person{
Car car; // 作为属性
void travel(){
car.run("北京");
}
}
class Car{
void run(String city){
System.out.println("汽车开到"+city);
}
}
5. 聚合
聚合是关联关系的一种特例,体现的是整体与部分的关系。整体由部分组成,比如说一个部门由多个员工组成。
public class Department{
Employee[] employees;
public static void main(String[] args){
Employee e1 = new Empleyee();
Employee e2 = new Empleyee();
Employee e3 = new Empleyee();
Department dept1 = new Department();
dept1.employees = new Employee[] {e1,e2,e3}
Department dept2 = new Department();
dept2.employees = new Employee[] {e1,e2}
}
}
class Empleyee{
}
部门由员工组成,同一个员工也可属于多个部门,并且部门解散后员工依旧存在,不会随之消亡。
6. 组合
组合关系是比聚合关系要求更高的一种关联关系,也是整体与部分,但是整体与部分不可分离,整体消亡部分消亡。例如汽车与其零件。
public class Car{
Engine engine = new Engine();
Chassis chassis = new Chassis();
Bodywork bodywork = new Bodywork();
Circuitry circuitry = new Circuitry();
}
class Engine{
}
class Chassis{
}
class Bodywork{
}
class Circuitry{
}