一、类的成员之三:构造器(必须掌握)
1.1 构造器有什么用
构造器的作用是为实例对象的实例变量初始化。在new对象的时候用。
1.2 构造器长什么样?
- 构造器的名称
必须
与所在的类名完全一致,包括大小写。 - 构造器
没有返回值类型
。不写void,也不写int,String等类型。一旦你写了返回值类型,它就是普通方法。
1.3 构造器有什么特点或要求?
- 所有类都有构造器
- 如果一个类没有手动编写构造器,那么编译器会给你自动添加一个默认的无参构造。
- 但是如果我们手动编写了构造器,那么编译器就不会再给你添加任何构造器了。
- 构造器可以重载。构造器有时候也叫做构造方法,或构造函数。
- 构造器的修饰符只能是public、protected、缺省、private,不能加其他修饰符(static,final,abstract等)
1.4 构造器如何快速的创建?
Alt + Insert
二、面向对象的基本特征之一:封装
2.1 为什么要封装?
生活中,快递为什么有包装盒/包装袋?
- 私密性:保护隐私
- 安全性:避免损坏
- 方便运输
- ......
Java中的类及其成员,也需要封装:
-
广义的封装概念:边界感,
- 例如:把一类事物的共同特征封装到一个类中,把一个完整的功能封装到一个方法中。
- 组件之间的封装,例如:项目中用到第三方的支付功能,微信、支付宝、银行等,只能调用对方开放的接口,无法获取内部的实现细节。
-
狭义的封装:对类或成员加权限修饰符,控制它们的可见性范围
- 隐藏实现细节,便于使用
- 安全
arduino
package com.test.constructor;
public class Student {
private String name;//属性私有化
private int age;//属性私有化
public Student() {//无参构造
}
public Student(String name, int age) {//有参构造
this.name = name;
this.age = age;
}
public void setAge(int age){
if(age >= 18 && age<=35) {
this.age = age;
}else{
System.out.println("年龄不合法!");
}
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age;
}
}
csharp
package com.test.constructor;
public class TestStudent {
public static void main(String[] args) {
//调用Student类的无参构造创建Student对象
Student s1 = new Student();
System.out.println(s1.getInfo());
//调用Student类的有参构造创建Student对象
Student s2 = new Student("张三",23);
System.out.println(s2.getInfo());
//s2.age = -18;//不安全
s2.setAge(-18);//安全
System.out.println(s2.getInfo());
s2.setAge(30);//安全
System.out.println(s2.getInfo());
}
}
2.2 四种权限修饰符
对类或成员加权限修饰符,控制它们的可见性范围。
private(私有的) | 缺省(不写) | protected(受保护) | public(公共的) | |
---|---|---|---|---|
本类 | √ | √ | √ | √ |
本包其他类 | × | √ | √ | √ |
其他包的子类(继承) | × | × | √ | √ |
其他包的非子类 | × | × | × | √ |
2.3 get/set方法
-
set方法:修改某个或某些属性的值
- 如果是静态变量的set方法,那么出现局部变量与静态变量重名时,通过"类名.静态变量"进行区分
- 如果是实例变量的set方法,那么出现局部变量与实例变量重名时,通过"this.实例变量"进行区分
-
get方法:获取某个或某些属性的值
- 如果某个属性的类型是boolean,那么它的get方法,一般以is开头。
❝
问:有构造器,和get/set会不会冲突?
答:构造器在new对象
时
,赋初始值。
- set方法在new对象
后
,用于修改属性的值。- get方法在new对象
后
,用于获取某个属性的值。
- 用快捷键生成构造器、get/set方法 Alt + Insert
三、标准Javabean
bean:豆。
Java:产咖啡的印尼的爪哇岛。
Javabean:咖啡豆,Java类。
什么是标准Javabean?
- 属性私有化
- 提供无参构造器。有参构造可选。因为后期Java对象的创建通常都是交给Spring等框架,而这些框架默认都是用无参构造创建对象。
- 提供合适的get/set方法
- 重写 equals和hashCode,toString方法
ini
package com.test.bean;
import java.util.Objects;
public class Employee {
private int id;
private String name;
private int age;
private char gender;
private double salary;
private String tel;
private String address;
public Employee() {
System.out.println("一个新员工入职");
}
public Employee(int id, String name, int age) {
this();//调用本类的其他构造器,调用无参构造
this.id = id;
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, char gender, double salary, String tel, String address) {
// this.id = id;
// this.name = name;
// this.age = age;
this(id,name,age);//调用本类的其他构造器,调用有参构造
this.gender = gender;
this.salary = salary;
this.tel = tel;
this.address = address;
}
//... 省略 get set...
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
", gender=" + gender +
", salary=" + salary +
", tel='" + tel + ''' +
", address='" + address + ''' +
'}';
}
//作用要明确,用于比较两个Employee对象的属性值是不是相同
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id && age == employee.age && gender == employee.gender && Double.compare(salary, employee.salary) == 0 && Objects.equals(name, employee.name) && Objects.equals(tel, employee.tel) && Objects.equals(address, employee.address);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + Objects.hashCode(name);
result = 31 * result + age;
result = 31 * result + gender;
result = 31 * result + Double.hashCode(salary);
result = 31 * result + Objects.hashCode(tel);
result = 31 * result + Objects.hashCode(address);
return result;
}
}
csharp
package com.test.bean;
public class TestEmployee {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee(2,"熊二",25);
Employee e3 = new Employee(3,"张三",23,'男',15000,"10086","北京");
//toString()作用等价于原来的getInfo()
//但是它比getInfo()方便,打印对象时,不用手动调用,它会自动调用
/* System.out.println(e1.toString());
System.out.println(e2.toString());
System.out.println(e3.toString());*/
System.out.println(e1);//如果没有写toString方法,那么打印的是地址值
System.out.println(e2);
System.out.println(e3);
Employee e4 = new Employee(2,"熊二",25);
System.out.println(e2 == e4);//false
//e2和e4是地址值,这里比较的是两个对象的地址值
System.out.println(e2.equals(e4));//比较两个对象是否相等 false
//默认情况下,equals等价于 ==
//如果不想让equals方法等价于==,就必须重写
System.out.println(e1.equals(e2));//false
}
}
四、对象数组
1、什么是对象数组
把对象存到数组中,这样的数组就称为对象数组。其实,它和普通的数组是一样的,只是因为对象数组使用起来相对复杂一点。
2、如何声明和使用
ini
元素的类型[] 数组名; //此时元素的类型是类类型,例如:Student,Rectangle,String等
静态初始化:
ini
元素的类型[] 数组名 = {对象1,对象2,对象3};
动态初始化:
ini
元素的类型[] 数组名 = new 元素的类型[长度];
数组的遍历:
css
for(int i=0; i<数组名.length; i++){
数组名[i]是元素,此时元素是对象
}
五、面向对象的基本特征之二:继承
5.1 什么是继承?
生活中:
- 基因
- 财产
- 才艺
继承:延续性、扩展性
继承上一代的优良传统,并发扬光大。青出于蓝而胜于蓝。
Java中为什么要有继承的设计呢?
- 代码的复用性:子类可以
复用
父类的代码 - 代码的扩展性:子类可以
重写
父类的方法或扩展
父类没有的成员 - 表示事物之间的is-a关系。例如:Student类继承Person类,Student is a Person.学生类是人类的一个分支。
5.2 如何继承?(重要)
arduino
【修饰符】 class 父类{
}
javascript
【修饰符】 class 子类 extends 父类{//子类是从父类中延伸出来的新的分类,更具体的分类
}
父类:SuperClass,称为父类或超类 ,基类。
子类:SubClass,称为子类或派生类。
5.3 继承的特点或要求(重要)
1、Java中只允许单继承
。比喻:每一个子类只有1个亲生父亲。
2、但是Java中支持多层继承
。比喻:代代相传。你的父亲仍然有父亲。爷爷的特征到孙子类仍然是保留的。
3、Java中一个父类可以同时有多个子类。比喻:支持多胎。支持家族兴旺。
4、父类的所有成员变量、成员方法都会继承到子类中。因为成员变量代表事物的数据特征,成员方法代表事物的行为/功能特征,而子类是父类这个事物分类下的一个分支,所以这些特征都会延续下来。但是
,父类中私有的成员变量、成员方法,在子类中不能直接
使用,可以间接使用。
5、父类的构造器不会继承
到子类中。但是
,子类的构造器中又必须调用
父类的构造器。因为子类继承了父类声明的所有成员变量,那么创建子类对象时就需要为这些成员变量初始化,而为这些成员变量初始化的代码已经在父类的构造器中写过了,可以不用重复编写这些代码了,直接调用它们即可。
- super():表示调用父类的无参构造。这句代码可以省略。
- super(实参列表):明确表示调用父类的有参构造。这句代码不能省略,一旦省略,就表示调用无参构造了。
- 它们必须在子类构造器的首行。
案例一:子类直接用父类的成员
arduino
package com.test.inherited;
public class Person {
public String name;
public int age;
public String getPersonInfo(){
return "姓名:" + name + ",年龄:" + age;
}
}
java
package com.test.inherited;
public class Student extends Person{
public int score;//成绩
public String getStudentInfo(){
return "姓名:" + name + ",年龄:" + age +",成绩:" + score;
//直接使用父类非private的属性
}
}
java
package com.test.inherited;
public class Student extends Person{
public int score;//成绩
public String getStudentInfo(){
return getPersonInfo() +",成绩:" + score;
//间接使用父类非private的方法
}
}
案例二:子类间接使用父类的成员
arduino
package com.test.inherited;
public class Person {
private String name;
private int age;
public String getPersonInfo(){
return "姓名:" + name + ",年龄:" + age;
}
}
ruby
package com.test.inherited;
public class Student extends Person{
public int score;//成绩
public String getStudentInfo(){
//return "姓名:" + name + ",年龄:" + age +",成绩:" + score;
//报错,因为name和age此时在父类Person中是private
return getPersonInfo() +",成绩:" + score;
//直接调用父类的方法,从而间接使用父类的私有属性
}
}
5.4 方法的重写(重要)
1、什么是方法的重写?
方法的重写(Override)是指子类覆盖/重写/覆写父类的某个方法。因为子类会继承父类的所有方法,但是某些方法的方法体功能实现不适用于子类,那么子类就可以重新实现它。
2、如何重写方法?
-
修饰符:
-
权限修饰符:public、protected、缺省、private,其中private的方法是不能被重写的。并且子类重写时,方法的权限修饰符必须
大于等于
父类被重写方法的权限修饰符。- 父类被重写方法是public,子类这个方法只能是public。
- 父类被重写方法是protected,子类这个方法可以是public,protected
- 父类被重写方法是缺省,子类这个方法可以是public,protected,缺省
-
其他修饰符:static,静态方法不能被重写。
-
-
返回值类型:
-
void和基本数据类型:子类必须保持一致
-
引用数据类型:子类重写时,方法的返回值类型可以是
小于等于
它- 例如父类被重写方法的返回值类型是Person,那么子类重写方法的返回值类型可以是Person,也可以是Student
-
-
方法名:
必须完全相同
-
形参列表:
必须完全相同
,这里相同是指类型、个数、顺序。不包括形参名。 -
方法体:子类重写就是为了重新实现方法体,根据功能的需求来。
3、如何调用父类被重写的方法?
super.父类被重写方法
4、方法重载与重写的区别
方法的重载 | 方法的重写 | |
---|---|---|
位置 | 同一个类 或 父子类 | 父子类 |
英文单词 | Overload | Override |
修饰符 | 不看 | 不能重写private和static的方法。权限修饰符必须满足 >= |
返回值类型 | 不看 | void和基本数据类型:必须相同 引用数据类型:满足<= |
方法名 | 必须相同 | 必须相同 |
形参列表 | 必须不同 | 必须相同 |
5.5 根父类(了解)
Object是java.lang包下的一个类,它是所有Java类的根,老祖宗。我们称为根父类。
Object类的方法,所有子类都会继承,即所有子类都有这些方法。但是,对于toString,equals和hashCode方法来说,子类通常都会重写。
java
//重写equals,hashCode,toString
//快捷键Alt + Insert 或 Ctrl + O
@Override //这个是注解。它是用于标记以下方法是重写的意思,它会告诉编译器,让编译器对该方法做严格的格式检查,看它是否满足重写的要求
//但是,如果你本身没有违反重写的要求,@Override可以去掉。
//加或不加@Override它,不影响重写的本质。只是格式检查是不是彻底的问题。
//建议重写方法上保留它。
5.6 特殊关键字
5.6.1 native(了解)
native修饰的方法,称为本地方法,或内置的方法,它是内置在JVM相关的底层代码中,通过C/C++语言实现的,不是用Java语言实现。在Java层面看不到它的方法体。
本地方法的执行会自动调用本地方法库。本地方法的执行会在本地方法栈中开辟独立的内存空间。Java虚拟机中的栈空间分为两块:虚拟机栈(服务于Java的方法)和本地方法栈(服务于C/C++的函数)。
❝
提示:native的方法,虽然看不到它的源码,但是(1)在Java中可以正常调用。(2)如果子类继承了本地方法,只要它不是private,不是static,不是final,子类可以对它用Java代码进行重写。即本地方法在使用层面,把它当成普通的方法即可。
5.6.2 final(重要)
final:最终。
在Java中,它是修饰符,可以用于修饰:类、方法、变量。
修饰符类 | 修饰符方法 | 修饰变量 | |
---|---|---|---|
作用 | 这个类不能被继承(比喻:太监类) | 这样的方法不能被重写(可以被继承) | 这样的变量称为常量,值不能被修改。 |
举例 | String,Math,System等 | Object类中的getClass() |
❝
对于final修饰变量来说:
(1)final可以修饰局部变量,可以修饰静态变量,可以修饰实例变量
(2)静态变量 + final 建议大写,其他的变量加final一般不大写
(3)静态变量和实例变量 + final之后,它们都没有set方法,可以有get方法
(4)实例变量 + final,可以在变量后面直接写 = 值,也可以在构造器中进行初始化。
修改单词大小写的快捷键:Ctrl+Shift+U
六、抽象类(重要)
6.1 什么是抽象类?
语法层面来说:有abstract修饰的类,就是抽象类。
csharp
【权限修饰符】 abstract class 类名{
}
6.2 为什么要有抽象类?
父类是代表很多子类共同的特征,把很多子类共同的属性、方法抽取到父类中,从而避免在不同的子类中重复声明。
随着父类共同方法的抽取越来越抽象,就会出现抽象方法,即在父类中写不了具体方法体的方法,这样方法必须用abstract修饰,表示没有方法体的抽象方法,Java中规定,包含抽象方法的类必须是抽象类,这样的类是不能直接new对象
的。
不能对抽象类直接new,如果可以 new 对象,就可以直接通过对象调用抽象方法,但是抽象方法没有方法体可执行。
❝
问:抽象类有构造器吗?
答:一定有。此时构造器是给子类用的。
6.3 抽象类怎么用
抽象类的存在的意义:
-
它代表所有子类共同的特征
-
抽象类就是用来被继承的,子类继承抽象类,必须
重写抽象类的所有抽象方法
,否则子类也是是抽象类 -
重新父类抽象方法快捷键
- Ctrl + O 可以重写父类抽象的和非抽象的方法
- Ctrl + I 重写父类的抽象方法
七、接口(重要)
7.1 什么是接口?
生活中:USB接口,电源插座的接口等,代表连接口。其实还代表了"标准"规范的意思。
Java中的接口也是代表(1)连接口(2)行为规范,方法的规范。
例如:后期做项目时,会调用银行接口,支付宝接口,微信支付接口来完成支付功能。
例如:后端服务器与前端的界面之间要约定双方传数据的规范,标准,这是也是接口的一部分。
从语法层面,说接口是用interface关键字声明的类型。
7.2 如何声明接口
csharp
【修饰符】 interface 接口名{
}
7.3 接口的成员
回忆类的成员:
scss
(1)成员变量:静态变量和实例变量
(2)成员方法:静态方法和非静态方法
(3)构造器:无参构造和有参构造
(4)代码块
(5)内部类
接口的成员有限制,明显与类不同:
JDK8之前:
- 公共的静态的常量:public static final (3个关键字可以省略),因为接口是代表一种标准规范,那么具体的值必须是明确的,写死的。
- 公共的抽象方法`:public abstract(2个关键字可以省略),因为接口是代表一种标准规范,只是说它有什么功能,但是功能的具体实现要有子类来实现。抽象方法是没有方法体,子类必须实现/重写它。
JDK8之后:
-
公共的静态方法
:public static(其中public可以省略,static不能省略),静态方法必须有方法体。静态方法的调用不需要接口的对象。 -
公共的默认方法
:public default(其中public可以省略,default不能省略)- 为了接口的升级,Java允许定义默认方法。默认方法是可以写方法体的,子类可以选择实现它,也可以选择不实现它。
- 如果没有默认方法,按照之前的语法规则,接口中只能增加抽象方法,一旦父接口增加抽象方法,就会影响所有的实现类。
JDK9之后:
- 私有的方法(了解):用于表示接口中内部使用的方法,通常是因为两个静态方法,或两个默认方法之间有共同代码,抽取出来的一个内部方法,这段代码只是功能的一部分,不是完整的功能,不希望外部调用,所以私有化。
7.4 接口的特点
- 接口不能直接new对象
- 接口允许多实现
- 接口与接口支持多继承
❝
问:抽象类与接口有什么区别?
相同的:它们都不能new对象,因为它们里面都可能有抽象方法。
不同点:
(1)成员不同(见上)
(2)类与类之间有"单继承"的限制,需要通过接口来解决这个问题,接口允许多实现
7.5 如何使用接口的各个成员
- 静态的,用接口名.静态成员
- 非静态的,创建子类对象,然后对象名.非静态成员
7.6 接口的作用/意义
因为接口允许多实现,可以解决抽象类单继承的限制问题
。
类与接口之间可以是简单的has-a或like-a的关系,而不是死板的is-a的关系。
-
如果是is-a的关系,在逻辑层面要求比较严格。
- Bird is a Animal。
- Student is a Person。
- Circle is a Shape。
-
而has-a或like-a的关系的话,我们只要某个类想要拥有这个接口的方法,那么就可以继承它,不用管逻辑关系。
- Bird has a fly方法。
- Plane has a fly方法。
- Kite has a fly方法。
- Superman has a fly方法。
- UFO like a flyable things。
例如:连接wifi,不一定是电脑,手机。可以是任意设备,只要遵循wifi的通信规则设计连网能力,家里任何设备都可以连接wifi。