1.类和对象
客观存在的事物皆为对象 ,即万物皆对象。
1.1类与对象的理解
类的理解:
类是对现实生活中一类具有共同属性和行为的事物的抽象。
类是对象的数据类型,类是具有相同属性和行为的一组对象的集合。
类就是对现实事物的一种描述。
类的组成:
属性:指事物的特征,例如:学生(学号,姓名,年龄)。
行为:指事物能执行的操作,例如:学生(学习,做题)。
类和对象的关系:
类:类是对现实生活中一类具有共同属性和行为的事物的抽象。
对象:是能够看得到摸的着的真实存在的实体。
类是对事物的一种描述,对象则为具体存在的事物。
1.2 类的定义
类的组成是由属性和行为两部分组成。
属性:在类中通过成员变量来体现(成员变量:类中方法外的变量)。
行为:在类中通过成员方法来体现(成员方法:和前面的方法相比去掉static关键字即可)。
类的定义步骤:
①定义类。
②编写类的成员变量。
③编写类的成员方法。
/* 类的定义格式 */
public class 类名 {
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
…
// 成员方法
方法1;
方法2;
}
示例代码:
/*
学生类:
类名:
学生(Student)
成员变量:
学号(id)
姓名(name)
成员方法:
学习(study)
做作业(doHomework)
*/
public class Student {
//成员变量
int id;
String name;
//成员方法
public void study() {
System.out.println("学习");
}
public void doHomework() {
System.out.println("做作业");
}
}
1.3 对象的使用
创建对象的格式:
类名 对象名 = new 类名();
调用成员的格式:
调用成员变量:
对象名.成员变量;
调用成员方法:
对象名.成员方法();
示例代码:
package com.poiwens.notes;
/*
创建对象
格式:类名 对象名 = new 类名();
范例:Phone p = new Phone();
使用对象
1:使用成员变量
格式:对象名.变量名
范例:p.brand
2:使用成员方法
格式:对象名.方法名()
范例:p.call()
*/
public class StudentDemo {
public static void main(String[] args) {
//创建对象
Student p = new Student();
//使用成员变量
System.out.println(p.id);
System.out.println(p.name);
p.id = 1001;
p.name = "张三";
System.out.println(p.id);
System.out.println(p.name);
//使用成员方法
p.study();
p.doHomework();
}
}
2. 对象内存
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份。
3. 成员变量和局部变量
3.1 成员变量和局部变量的区别
类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
内存中位置不同:成员变量(堆内存)局部变量(栈内存)
生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
4. 封装
4.1 封装思想
- 封装概述
是面向对象三大特征之一(封装,继承,多态)
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
多个类都有共同的属性,于是创建一个共同的父类去继承数据。
对象代表什么,就得封装对应的数据,并提供数据对应的行为
-
封装代码实现
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法
4.2 private关键字
private是一个修饰符,可以用来修饰成员(成员变量,成员方法)。
被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作:
提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰。
提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰。
示例代码:
package com.poiwens.test;
/**
* 类的描述:学生类
*/
public class Student {
//成员变量
private String name;
private int age;
// set/get方法
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
}
package com.poiwens.test;
/**
* 类的描述:学生测试类
*/
public class StudentDemo {
//入口
public static void main(String[] args) {
//创建对象
Student s = new Student();
//set方法给成员变量赋值
s.setAge(18);
s.setName("张三");
//get方法获取值并输出
System.out.println(s.getAge() + "-" + s.getName());
}
}
4.3 this关键字
this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)。
this代表所在类的当前对象的引用(地址值),即代表当前对象。
方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量。
方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量。
package com.poiwens.test;
/**
* 类的描述:学生类
*/
public class Student {
//成员变量
private String name;
private int age;
// set/get方法
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
}
4.3.1 this关键字的应用
4.3.1.1 用于普通的gettter与setter方法
this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
4.3.1.2 用于构造方法中
this出现在构造方法中,代表构造方法正在初始化的那个对象。\
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
}
4.4 封装的步骤
1.使用 private 关键字来修饰成员变量。
2.使用public修饰getter和setter方法。
4.4.1 封装步骤的实现
1.private修饰成员变量
public class Student {
private String name;
private int age;
}
2.public修饰getter和setter方法
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
5. 构造方法
5.1 构造方法概述
构造方法(又名构造器)是一种特殊的方法。
5.1.1作用
创建对象 Student stu = new Student(参数);
5.1.2格式
public class 类名{
//构造方法
修饰符 类名(形参列表) {
// 构造体代码,执行代码
}
}
5.1.3功能:
主要是完成对象数据的初始化。
示例代码:
public class Student {
//成员变量
private String name;
private int age;
//无参构造方法
public Student() {
}
//含参构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
5.2 构造方法的注意事项
5.2.1 构造方法的创建
如果没有定义构造方法,系统将给出一个默认的无参数构造方法。
如果定义了构造方法,系统将不再提供默认的构造方法。
5.2.2 构造方法的重载
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法。
5.2.3 推荐的使用方式
无论是否使用,都手动书写无参数构造方法。
5.2.4 重要功能
可以使用带参构造,为成员变量进行初始化。
示例代码:
package com.poiwens.test;
/**
* 类的描述:学生测试类
*/
public class StudentDemo {
//入口
public static void main(String[] args) {
//创建对象
//无参构造方法
Student s1 = new Student();
//set方法给成员变量赋值
s1.setAge(18);
s1.setName("张三");
//get方法获取值并输出
System.out.println(s1.getAge() + "-" + s1.getName());
//有参构造方法
Student s2 = new Student("李四", 20);
//输出s2
System.out.println(s2.getAge() + "-" + s2.getName());
}
}
5.3 标准类制作
① 类名需要见名知意。
② 成员变量使用private修饰。
③ 提供至少两个构造方法 。
无参构造方法。
带全部参数的构造方法。
④ get和set方法
提供每一个成员变量对应的setXxx()/getXxx()。
⑤ 如果还有其他行为,也需要写上。
6. Static关键字
6.1 概述
我们已经知道面向对象中,存在类和对象的概念,我们在类中定义了一些成员变量,例如name,age,sex ,结果发现这些成员变量,每个对象都存在(因为每个对象都可以访问)。
而像name ,age , sex确实是每个学生对象都应该有的属性,应该属于每个对象。
所以Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。static关键字在Java开发非常的重要,对于理解面向对象非常关键。
关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。
6.2 定义格式和使用
static是静态的意思。 static可以修饰成员变量或者修饰方法。(放在成员变量或方法名前)
6.2.1 静态变量及其访问
有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
例如现在我们需要定义三年二班全部的学生类,那么这些学生类的对象的学校属性应该都是“三年二班”,这个时候我们可以把这个属性定义成static修饰的静态成员变量。
定义格式
修饰符 static 数据类型 变量名 = 初始值;
public class Student {
public static String schoolName = "三年二班"; // 属于类,只有一份。
// .....
}
静态成员变量的访问
格式:类名.静态变量
public static void main(String[] args){
System.out.println(Student.schoolName); // 三年二班
Student.schoolName = "一年二班";
System.out.println(Student.schoolName); // 一年二班
}
6.2.2 实例变量及其访问
无static修饰的成员变量属于每个对象的, 这个成员变量叫实例变量,之前我们写成员变量就是实例成员变量。
需要注意的是:实例成员变量属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例成员变量
6.2.3 静态方法及其访问
有static修饰成员方法,说明这个成员方法是属于类的,这个成员方法称为类方法或者静态方法。 直接用 类名访问即可。因为类只有一个,所以静态方法在内存区域中也只存在一份。所有的对象都可以共享这个方法。
与静态成员变量一样,静态方法也是直接通过类名.方法名称即可访问。
public class Student{
public static String schoolName = "三年二班"; // 属于类,只有一份。
// .....
public static void study(){
System.out.println("我们都在山工院学习");
}
}
静态成员方法的访问:
格式:类名.静态方法
public static void main(String[] args){
Student.study();
}
6.2.4 实例方法及其访问
无static修饰的成员方法属于每个对象的,这个成员方法也叫做实例方法。
需要注意的是:实例方法是属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例方法
public class Student {
// 实例变量
private String name ;
// 2.方法:行为
// 无 static修饰,实例方法。属于每个对象,必须创建对象调用
public void run(){
System.out.println("学生可以跑步");
}
// 无 static修饰,实例方法
public void sleep(){
System.out.println("学生睡觉");
}
public static void study(){
}
}
public static void main(String[] args){
// 创建对象
Student stu = new Student ;
stu.name = "张三";
// Student.sleep();// 报错,必须用对象访问。
stu.sleep();
stu.run();
}
6.3 小结
1.当 static 修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
7. 继承
7.1 概述
继承:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
假如我们要定义如下类:
学生类,老师类和班主任类,分析如下。
1.学生类
属性:姓名,年龄
行为:吃饭,睡觉
2.老师类
属性:姓名,年龄,薪水
行为:吃饭,睡觉,教书
3.班主任
属性:姓名,年龄,薪水
行为:吃饭,睡觉,管理
如果我们定义了这三个类去开发一个系统,那么这三个类中就存在大量重复的信息(属性:姓名,年龄。行为:吃饭,睡觉)。这样就导致了相同代码大量重复,代码显得很臃肿和冗余,那么如何解决呢?
假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
其中,多个类可以称为子类,单独被继承的那一个类称为父类、超类(superclass)或者基类。
7.1.1 继承的好处
1.提高代码的复用性(减少代码冗余,相同代码重复利用)。
2.使类与类之间产生了关系。
7.2 继承的格式
通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
注意:
Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
7.3 继承案例
7.3.1 案例
请使用继承定义以下类:
- 学生类
属性:姓名,年龄
行为:吃饭,睡觉 - 老师类
属性:姓名,年龄,薪水
行为:吃饭,睡觉,教书 - 班主任
属性:姓名,年龄,薪水
行为:吃饭,睡觉,管理
老师类,学生类,还有班主任类,实际上都是属于人类的,我们可以定义一个人类,把他们相同的属性和行为都定义在人类中,然后继承人类即可,子类特有的属性和行为就定义在子类中了。
7.3.2 代码实现
1.父类Human类
public class Human {
// 合理隐藏
private String name ;
private int age ;
// 合理暴露
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2.子类Teacher类
public class Teacher extends Human {
// 工资
private double salary ;
// 特有方法
public void teach(){
System.out.println("老师在认真教技术!");
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
3.子类Student类
public class Student extends Human{
}
4.子类HeadTeacher类
public class HeadTeacher extends Human {
// 工资
private double salary ;
// 特有方法
public void admin(){
System.out.println("班主任强调纪律问题!");
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
5.测试类
public class Test {
public static void main(String[] args) {
Teacher zhangSan = new Teacher();
zhangSan.setName("张三");
zhangSan.setAge("31");
zhangSan.setSalary(1000.99);
System.out.println(zhangSan.getName());
System.out.println(zhangSan.getAge());
System.out.println(zhangSan.getSalary());
zhangSan.teach();
BanZhuRen liSi = new HeadTeacher();
liSi.setName("李四");
liSi.setAge("28");
liSi.setSalary(1000.99);
System.out.println(liSi.getName());
System.out.println(liSi.getAge());
System.out.println(liSi.getSalary());
liSi.admin();
Student wangWu = new Student();
wangWu.setName("王五");
wangWu.setAge("18");
//wangWu.setSalary(1000.99); // xugan没有薪水属性,报错!
System.out.println(wangWu.getName());
System.out.println(wangWu.getAge());
}
}
7.4 小结
1.继承实际上是子类相同的属性和行为可以定义在父类中,子类特有的属性和行为由自己定义,这样就实现了相同属性和行为的重复利用,从而提高了代码复用。
2.子类继承父类,就可以直接得到父类的成员变量和方法。
7.5 子类不能继承的内容
并不是父类的所有内容都可以给子类继承的:
子类不能继承父类的构造方法。
值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
public class Demo {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z.num1);
// System.out.println(z.num2); // 私有的子类无法使用
// 通过getter/setter方法访问父类的private成员变量
System.out.println(z.getNum2());
z.show1();
// z.show2(); // 私有的子类无法使用
}
}
class Fu {
public int num1 = 10;
private int num2 = 20;
public void show1() {
System.out.println("show1");
}
private void show2() {
System.out.println("show2");
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
}
class Zi extends Fu {
}
7.6 继承后的特点—成员变量
7.6.1 成员变量不重名
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
class Fu {
// Fu中的成员变量
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num2 = 6;
// Zi中的成员方法
public void show() {
// 访问父类中的num
System.out.println("Fu num="+num); // 继承而来,所以直接访问。
// 访问子类中的num2
System.out.println("Zi num2="+num2);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show方法
z.show();
}
}
演示结果:
Fu num = 5
Zi num2 = 6
7.6.2 成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。
class Fu1 {
// Fu中的成员变量。
int num = 5;
}
class Zi1 extends Fu1 {
// Zi中的成员变量
int num = 6;
public void show() {
// 访问父类中的num
System.out.println("Fu num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + num);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi1 z = new Zi1();
// 调用子类中的show方法
z1.show();
}
}
演示结果:
Fu num = 6
Zi num = 6
子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果此时想访问父类成员变量可以使用super关键字。
7.6.3 super访问父类成员变量
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super 关键字,修饰父类成员变量,类似于之前学过的 this 。
需要注意的是:super代表的是父类对象的引用,this代表的是当前对象的引用。
使用格式:
super.父类成员变量名;
class Fu {
// Fu中的成员变量。
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
int num = 1;
// 访问方法中的num
System.out.println("method num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + this.num);
// 访问父类中的num
System.out.println("Fu num=" + super.num);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi1 z = new Zi1();
// 调用子类中的show方法
z1.show();
}
}
演示结果:
method num=1
Zi num=6
Fu num=5
Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量.访问父类的私有成员变量可以在父类中提供公共的getXxx方法和setXxx方法。
7.7 继承后的特点—成员方法
7.7.1 成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
class Fu {
public void show() {
System.out.println("Fu类中的show方法执行");
}
}
class Zi extends Fu {
public void show2() {
System.out.println("Zi类中的show2方法执行");
}
}
public class Demo05 {
public static void main(String[] args) {
Zi z = new Zi();
//子类中没有show方法,但是可以找到父类方法去执行
z.show();
z.show2();
}
}
7.7.2 成员方法重名
如果子类父类中出现重名的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。
class Fu {
public void show() {
System.out.println("Fu show");
}
}
class Zi extends Fu {
//子类重写了父类的show方法
public void show() {
System.out.println("Zi show");
}
}
public class ExtendsDemo05{
public static void main(String[] args) {
Zi z = new Zi();
// 子类中有show方法,只执行重写后的show方法
z.show(); // Zi show
}
}
7.8 继承后的特点—构造方法
7.8.1 概念
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。**子类的构造方法中默认有一个
super(),**表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子)
继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法。
7.8.2 案例演示
按如下需求定义类:
- 人类
成员变量: 姓名,年龄
成员方法: 吃饭 - 学生类
成员变量: 姓名,年龄,成绩
成员方法: 吃饭
class Person {
private String name;
private int age;
public Person() {
System.out.println("父类无参");
}
// getter/setter省略
}
class Student extends Person {
private double score;
public Student() {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
}
public class Demo07 {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("----------");
Student s2 = new Student(99.9);
}
}
输出结果:
父类无参
子类无参
----------
父类无参
子类有参
7.8.3 小结
子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
子类构造方法的第一行都隐含了一个**super()**去调用父类无参数构造方法,**super()**可以省略不写。
7.9 继承的特点
Java只支持单继承,不支持多继承。
// 一个类只能有一个父类,不可以有多个父类。
class A {}
class B {}
class C1 extends A {} // ok
// class C2 extends A, B {} // error
一个类可以有多个子类。
// A可以有多个子类
class A {}
class C1 extends A {}
class C2 extends A {}
可以多层继承。
class A {}
class C1 extends A {}
class D extends C1 {}
顶层父类是Object类。所有的类默认继承Object,作为父类。
8. 方法重写
8.1 概念
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
8.2 使用场景
发生在子父类之间的关系。
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方法。
8.2.1 动物类
public class Animal {
public void run(){
System.out.println("动物跑");
}
public void cry(){
System.out.println("动物叫");
}
}
然后定义一个猫类,猫可能认为父类cry()方法不能满足自己的需求。
8.2.2 猫类
public class Cat extends Animal {
public void cry(){
System.out.println("猫叫!");
}
}
public class Test {
public static void main(String[] args) {
// 创建子类对象
Cat ddm = new Cat();
// 调用父类继承而来的方法
ddm.run();
// 调用子类重写的方法
ddm.cry();
}
}
8.3 @Override重写注解
@Override:注解,重写注解校验!
这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
public class Cat extends Animal {
// 声明不变,重新实现
// 方法名称与父类全部一样,只是方法体中的功能重写写了!
@Override
public void cry(){
System.out.println("猫叫");
}
}
8.4 注意事项
- 方法重写是发生在子父类之间的关系。
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
9. super()和this()
class Person {
private String name;
private int age;
public Person() {
System.out.println("父类无参");
}
// getter/setter省略
}
class Student extends Person {
private double score;
public Student() {
//super(); // 调用父类无参构造方法,默认就存在,可以不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {
//super(); // 调用父类无参构造方法,默认就存在,可以不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
// getter/setter省略
}
public class Demo07 {
public static void main(String[] args) {
// 调用子类有参数构造方法
Student s2 = new Student(99.9);
System.out.println(s2.getScore()); // 99.9
System.out.println(s2.getName()); // 输出 null
System.out.println(s2.getAge()); // 输出 0
}
}
可以发现,子类有参数构造方法只是初始化了自己对象中的成员变量score,而父类中的成员变量name和age依然是没有数据的,可以借助与super(…)去调用父类构造方法,以便初始化继承自父类对象的name和age。
9.1 super和this的用法格式
一般用法:
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
调用构造方法:
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
9.2 super(…)用法演示
class Person {
private String name ="李二";
private int age = 20;
public Person() {
System.out.println("父类无参");
}
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
// getter/setter省略
}
class Student extends Person {
private double score = 100;
public Student() {
//super(); // 调用父类无参构造方法,默认就存在,可以不写,必须再第一行
System.out.println("子类无参");
}
public Student(String name, int age, double score) {
super(name ,age);// 调用父类有参构造方法Person(String name , int age)初始化name和age
this.score = score;
System.out.println("子类有参");
}
// getter/setter省略
}
public class Demo07 {
public static void main(String[] args) {
// 调用子类有参数构造方法
Student s2 = new Student("张三",20,99);
System.out.println(s2.getScore()); // 99
System.out.println(s2.getName()); // 输出 张三
System.out.println(s2.getAge()); // 输出 20
}
}
注意:
子类的每个构造方法中均有默认的super(),用于调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
super(…)是根据参数去确定调用父类哪个构造方法的。
父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造调用时,一定先调用父类的构造。
9.3 this(…)用法演示
默认是去找本类中的其他构造方法,根据参数来确定具体调用哪一个构造方法。
为了借用其他构造方法的功能。
public class ThisDemo {
public static void main(String[] args) {
Student zhangSan = new Student();
System.out.println(xuGan.getName()); // 输出:张三
System.out.println(xuGan.getAge());// 输出:21
System.out.println(xuGan.getSex());// 输出: 男
}
}
class Student{
private String name ;
private int age ;
private char sex ;
public Student() {
// 调用其他构造方法:Student(String name, int age, char sex),赋予初值
this("张三",21,'男');
}
public Student(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}
9.4 小结
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
super(…)和this(…)是根据参数去确定调用父类哪个构造方法的。
super(…)可以调用父类构造方法初始化继承自父类的成员变量的数据。
this(…)可以调用本类中的其他构造方法。
10. 多态
10.1 多态的形式
多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
10.2 多态的使用场景
如果没有多态,在下图中register方法只能传递学生对象,其他的Teacher和administrator对象是无法传递给register方法方法的,在这种情况下,只能定义三个不同的register方法分别接收学生,老师和管理员。
public void register(Student s) {
//代码
}
有了多态之后,方法的形参就可以定义为共同的父类Person。
public void register(Person p) {
//代码
}
要注意的是:
当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象。
而且多态还可以根据传递的不同对象来调用不同类中的方法。
public void register(Person p) {
p.show; //根据传递对象的不同,调用不同的show方法
}
父类:
public class Person {
private String name;
private int age;
空参构造
带全部参数的构造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子类1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为:" + getName() + ", " + getAge());
}
}
子类2:
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:" + getName() + ", " + getAge());
}
}
子类3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:" + getName() + ", " + getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
}
10.3 多态的定义和前提
多态: 是指同一行为,具有多个不同表现形式。
从上面案例可以看出,Cat和Dog都是动物,都有吃这一行为,但是出现的效果(表现形式)是不一样的。
前提【重点】
-
有继承或者实现关系
-
方法的重写【意义体现:不重写,无意义】
-
父类引用指向子类对象【格式体现】
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
10.4 多态的运行特点
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
10.5 多态的弊端
我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了。
class Animal{
public void eat(){
System.out.println("动物吃东西!")
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
class Test{
public static void main(String[] args){
Animal a = new Cat();
a.eat();
//a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
}
}
10.6 引用数据类型转换
10.6.1 为什么要转型
多态的写法无法访问子类独有功能。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。所以,想要调用子类特有的方法,必须做向下转型。
基本数据类型转换
自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14 ;
多态的转型分为**向上转型(自动转换)与向下转型(强制转换)**两种。
10.6.2 向上转型(自动转换)
向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
**原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。**所以子类范围小可以直接自动转型给父类类型的变量。
10.6.3 向下转型(强制转换)
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
10.6.4 案例演示
定义类:
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
10.6.5 转型的异常
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常。这是因为,明明创建的是Cat类型对象,运行时,当然不能转换成Dog对象的。
10.6.6 instanceof关键字
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验。
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
在类型转换前,可以先对 要转换的类型做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
10.6.7 instanceof新特性(JDK14)
//新特性
//先判断a是否为Dog(cat)类型,如果是,则强转成Dog(cat)类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
11. 包
11.1 包
包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。
包名的命名规范:
路径名.路径名.xxx.xxx
// 例如:com.poiwens.a
包名一般是公司域名的倒写。例如:百度是www.baidu.com,包名就可以定义成com.baidu.技术名称。
包名必须用”.“连接。
包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。
2.2 导包
什么时候需要导包?
情况一:在使用Java中提供的非核心包中的类时
情况二:使用自己写的其他包中的类时
什么时候不需要导包?
情况一:在使用Java核心包(java.lang)中的类时
情况二:在使用自己写的同一个包中的类时
2.3 使用不同包下的相同类
假设demo1和demo2中都有一个Student该如何使用?
//使用全类名的形式即可。
//全类名:包名 + 类名
//拷贝全类名的快捷键:选中类名crtl + shift + alt + c 或者用鼠标点copy,再点击copy Reference
com.poiwens.homework.demo1.Student s1 = new com.poiwens.homework.demo1.Student();
com.poiwens.homework.demo2.Student s2 = new com.poiwens.homework.demo2.Student();
12. 权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限。
public:公共的,所有地方都可以访问。
protected:本类 ,本包,其他包中的子类都可以访问。
默认(没有修饰符):本类 ,本包可以访问。
注意:默认是空着不写,不是default
private:私有的,当前类可以访问。
访问权限排列(由大到小):
public > protected > 默认 > private
12.1 不同权限的访问能力
| public | protected | 默认 | private | |
|---|---|---|---|---|
| 同一类中 | √ | √ | √ | √ |
| 同一包中的类 | √ | √ | √ | |
| 不同包的子类 | √ | √ | ||
| 不同包中的无关类 | √ |
public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private,隐藏细节。 - 构造方法使用
public,方便创建对象。 - 成员方法使用
public,方便调用方法。
不加权限修饰符,就是默认权限
13. final关键字
13.1 概述
学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。
如果有一个方法不想别人去改写里面内容,可以使用final关键字。
Java提供了final 关键字,表示修饰的内容不可变。
- final: 不可改变,最终的含义。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,有且仅能被赋值一次。
13.2 使用方式
13.2.1 修饰类
final修饰的类,不能被继承。
格式如下:
final class 类名 {
}
final class Fu {
}
// class Zi extends Fu {} // 报错,不能继承final的类
查询API发现像 public final class String 、public final class Math 、public final class Scanner 等,很多类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
13.2.2 修饰方法
final修饰的方法,不能被重写。
格式如下:
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
代码:
class Fu2 {
final public void show1() {
System.out.println("Fu2 show1");
}
public void show2() {
System.out.println("Fu2 show2");
}
}
class Zi2 extends Fu2 {
// @Override
// public void show1() {
// System.out.println("Zi2 show1");
// }
@Override
public void show2() {
System.out.println("Zi2 show2");
}
}
13.2.3 修饰变量-局部变量
局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
代码如下:
public class FinalDemo1 {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 10;
// 第二次赋值
a = 20; // 报错,不可重新赋值
// 声明变量,直接赋值,使用final修饰
final int b = 10;
// 第二次赋值
b = 20; // 报错,不可重新赋值
}
}
| public | protected | 默认 | private |
| ---------------- | ------ | --------- | ---- | ------- |
| 同一类中 | √ | √ | √ | √ |
| 同一包中的类 | √ | √ | √ | |
| 不同包的子类 | √ | √ | | |
| 不同包中的无关类 | √ | | | |
public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private,隐藏细节。 - 构造方法使用
public,方便创建对象。 - 成员方法使用
public,方便调用方法。
不加权限修饰符,就是默认权限
13. final关键字
13.1 概述
学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。
如果有一个方法不想别人去改写里面内容,可以使用final关键字。
Java提供了final 关键字,表示修饰的内容不可变。
- final: 不可改变,最终的含义。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,有且仅能被赋值一次。
13.2 使用方式
13.2.1 修饰类
final修饰的类,不能被继承。
格式如下:
final class 类名 {
}
final class Fu {
}
// class Zi extends Fu {} // 报错,不能继承final的类
查询API发现像 public final class String 、public final class Math 、public final class Scanner 等,很多类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
13.2.2 修饰方法
final修饰的方法,不能被重写。
格式如下:
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
代码:
class Fu2 {
final public void show1() {
System.out.println("Fu2 show1");
}
public void show2() {
System.out.println("Fu2 show2");
}
}
class Zi2 extends Fu2 {
// @Override
// public void show1() {
// System.out.println("Zi2 show1");
// }
@Override
public void show2() {
System.out.println("Zi2 show2");
}
}
13.2.3 修饰变量-局部变量
局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
代码如下:
public class FinalDemo1 {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 10;
// 第二次赋值
a = 20; // 报错,不可重新赋值
// 声明变量,直接赋值,使用final修饰
final int b = 10;
// 第二次赋值
b = 20; // 报错,不可重新赋值
}
}
1万+

被折叠的 条评论
为什么被折叠?



