一、复习
1.1 概念名称(以面向对象之后为主)
1、问题 1:成员变量与局部变量
成员变量 | 成员变量 | 局部变量 | |
---|---|---|---|
看有没有 static 修饰 | 静态变量(有) | 实例变量(没有) | 局部变量(没有) |
如何区分? | 在方法外 | 在方法外 | 在方法里面 |
跨类如何访问 | 类名.静态变量 | 先创建对象,然后通过 对象名.实例变量 | 无法跨类使用 |
是否有默认值 | 有 | 有 | 无 |
作用域 | 本类随便用 | 本类静态方法不能直接使用本类的实例变量 | 看 |
值是否共享 | 被本类所有对象共享 | 每一个对象都是独立的 | 仅限于当前方法 |
在内存中存在什么位置 | JDK7 及其之前:方法区永久代 JDK8 及其之后:堆 | 堆 | 栈 |
生命周期 | 从这个类被加载到内存就开始了 | 从 new 对象开始,到 GC(垃圾回收)结束 | 随方法入栈到方法出栈 |
2、构造器
构造器的特点:
- 构造器的名字必须与类名完全一致
- 构造器没有返回值类型,一定不能写返回值类型
- 构造器前面不能加 static 等其他修饰符,只能加 public、protected、缺省、private
- 如果程序员没有手动编写任何构造器,那么它会有一个默认的无参构造
- 如果程序员手动编写了构造器,那么你写几个构造器,它就有几个构造器
- 一个类如果有多个构造器,那么多个构造器之间是重载关系
- 构造器是用来在 new 对象的时候,为实例变量进行初始化
生成构造器的默认快捷键:Alt + Insert,选 Constructor
3、成员方法
静态方法 | 实例方法 | |
---|---|---|
有没有 static | 有 | 没有 |
本类中 | 只能使用本类的静态成员 | 本类的所有成员都可以 |
跨类 | 类名.静态方法 | 对象名.实例方法 |
本类的成员之间互相使用就遵循一个原则:
静态不能使用非静态
,其他随便用。
跨类使用成员(关于静态非静态)也只有一个原则:静态的用 “类名.”,非静态的先 new 对象,再用“对象名.”
4、面向对象的基本特征之一:封装
属性私有化是什么意思?
- 第一步:在成员变量之前加 private
- 第二步:提供 get/set 方法
- get:供类的外面来获取一个属性值
- set:供类的外面来修改一个属性值
5、权限修饰符有 4 种情况,3 个单词
权限修饰符有的人翻译为访问控制修饰符。
这些权限修饰符的可见性范围如下:
本类 | 本包 | 跨包的子类 | 跨包的非子类 | |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
二、对象数组
对象数组是用来存储一组对象的。
2.1 一维对象数组的声明和初始化
静态初始化语法格式:
数据类型[] 数组名 = {对象1,对象2,对象3};
//这里的数据类型是除了8种基本数据类型以外,通常是类
动态初始化语法格式:
数据类型[] 数组名 = new 数据类型[长度];
//这里的数据类型是除了8种基本数据类型以外,通常是类
动态初始化,数组元素是默认值。
元素的类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | \u0000 |
boolean | false |
引用数据类型 | null |
给对象数组的元素赋值:
数组名[下标] = 对象;
2.2 一维对象数组的遍历
1、普通 for 循环
快捷键:数组名.fori
for(int i=0; i<数组名.length; i++){
数组名[i]就是元素,此时元素是对象
}
2、增强 for 循环(请看 2.7 小节)
2.3 示例代码
package com.atguigu.array;
public class Student {
//实例变量
private String name;//姓名
private int score;//成绩
//无参构造
public Student() {
}
//有参构造
public Student(String name, int score) {
this.name = name;
this.score = score;
}
//get/set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//其他方法
public String display(){
return "姓名:" + name +",成绩:" + score;
}
}
静态初始化:
package com.atguigu.array;
public class TestStudent {
public static void main(String[] args) {
//Student[] students = {new Student("张三",89), new Student("李四",84), new Student("王五",90)};
Student s1 = new Student("张三", 89);
Student s2 = new Student("李四",84);
Student s3 = new Student("王五",90);
Student[] students = {s1, s2, s3};
for (int i = 0; i < students.length; i++) {
// System.out.println(students[i]);//这么写看到的是3个对象地址值
// System.out.println("姓名:" + students[i].getName() +",成绩:" + students[i].getScore());
System.out.println(students[i].display());
}
}
}
动态初始化:
package com.atguigu.array;
public class TestStudent2 {
public static void main(String[] args) {
Student[] students = new Student[3];
//上面这句代码创建的不是学生对象,而是数组对象
//比喻:上面这句代码准备了3个停车位,此时停车位上没有车
// 或者准备了3个新座位给同学,但是同学来没来
//此时数组的元素都是默认值null
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);//null
// System.out.println(students[i].display());
// 错误,因为students[i]是null,null表示没有指向任何学生对象,就无法获取到学生对象的姓名和成绩信息
//会发生空指针异常NullPointerException
}
//学生报到
students[0] = new Student("张三", 89);
students[1] = new Student("李四",84);
students[2] = new Student("王五",90);
//再次遍历
for (int i = 0; i < students.length; i++) {
// System.out.println(students[i]);//地址值
System.out.println(students[i].display());
}
}
}
2.4 一维数组的算法
1、找最大值
package com.atguigu.array;
public class TestStudent3 {
public static void main(String[] args) {
Student[] students = new Student[3];
//学生报到
students[0] = new Student("张三", 89);
students[1] = new Student("李四",84);
students[2] = new Student("王五",90);
//找出成绩最高的学生
//(1)第一步,先假设第1个元素最大
Student max = students[0]; //max现在代表一个学生对象
//(2)第二步:用max与剩下的元素挨个比较
for (int i = 1; i < students.length; i++) {
//比较:两个学生的成绩
// if(max < students[i])//错误, max和students[i]都是学生对象的首地址,地址值是无法比较大小的
if(max.getScore() < students[i].getScore()){
max = students[i];
}
}
//成绩最高的学生
// System.out.println("成绩最高的学生:" + max);//地址值
System.out.println("成绩最高的学生:" + max.display());//地址值
}
}
2、冒泡排序
package com.atguigu.array;
public class TestStudent4 {
public static void main(String[] args) {
Student[] students = new Student[3];
//学生报到
students[0] = new Student("张三", 89);
students[1] = new Student("李四",84);
students[2] = new Student("王五",90);
//按照成绩从低到高
//冒泡排序:(1)相邻元素比较(2)n个元素需要比较n-1轮
for(int i=1; i<students.length; i++){
/*
当i=1,
students[0] ~ students[1]
students[1] ~ students[2]
j = 0,1 j<3-1
当i=2
students[0] ~ students[1]
j=0 j<3-2
students[j] ~ students[j+1]
*/
for(int j=0; j<students.length - i; j++){
// if(students[j] > students[j+1]){//错误, students[j] 和 students[j+1]里面是地址值
if(students[j].getScore() > students[j+1].getScore()){
//交换 students[j] 和 students[j+1]
Student temp = students[j];
students[j] = students[j+1];
students[j+1] = temp;
}
}
}
//显示排序后的结果
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].display());
}
}
}
2.5 对象数组的内存分析
2.6 答疑:关于冒泡排序的 i 从 0 还是 1 开始
2.7 增强 for 循环
2.7.1 增强 for 循环的语法格式
增强 for 循环,又称为 foreach 循环。
增强 for 循环是一种语法糖,用于简化开发人员遍历数组或集合(集合是后面章节才会学习)的代码。
增强 for 循环的,快捷键:iter
for(元素的类型 元素名 : 数组名){
}
对比:
普通 for 循环,快捷键:数组名.fori
for(int i=0; i<数组名.length; i++){
数组名[i]就是元素,此时元素是对象
}
语法糖(Syntactic Sugar)是指编程语言中为方便开发者书写代码而提供的一种简化写法,它不会改变程序的功能,但在形式上更加简洁、易读。编译器或解释器在处理这些语法糖时,会将其转换为更基础的语法结构。
for-each 循环:
如
for(String s : 数组名)
,底层实际上是通过普通 for 循环实现的。如
for (String s : 集合名)
,底层实际上是通过迭代器实现的。(后面集合章节再讲)
package com.atguigu.foreach;
public class TestArray {
public static void main(String[] args) {
int[] arr = {10, 20, 30, 40, 50};
//普通for循环,快捷键 数组名.fori 或 itar
//遍历:挨个看看
System.out.println("普通for循环遍历:");
for (int i = 0; i < arr.length; i++) {//i可以当做数组下标使用
System.out.println(arr[i]);
}
//增强for循环
System.out.println("增强for循环遍历:");
//快捷键 iter
for (int i : arr) {//i是元素
System.out.println(i);
}
}
}
package com.atguigu.foreach;
import com.atguigu.array.Student;
public class TestObjectArrays {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("张三", 89);
students[1] = new Student("李四",84);
students[2] = new Student("王五",90);
//普通for循环
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].display());
}
System.out.println("=========================");
//增强for循环
for (Student student : students) {
System.out.println(student.display());
}
}
}
2.7.2 普通 for 循环与增强 for 循环的区别
普通 for 循环与增强 for 循环的区别:
普通 for 循环 | 增强 for 循环 | |
---|---|---|
语法格式 | for(int i=0; i<数组名.length; i++) {数组名[i]就是元素,此时元素是对象 } | for(元素的类型 元素名 : 数组名) {} |
是否有下标信息 | 有 | 无 |
是否可以替换元素 | 可以 | 不可以 |
是否可以修改元素的属性值 | 可以 | 可以(只要不换对象) |
如何选择 | 既要看元素, 又可能要修改元素,就用普通 for 循环 | 如果只是看元素,建议用增强 for 更简洁 |
适用场景 | 除了遍历数组,在其他需要重复执行代码的地方也可以用普通 for | 仅仅只能用于遍历数组或集合 |
package com.atguigu.foreach;
public class TestDifferent {
public static void main(String[] args) {
//区别
// (1)普通for循环有下标,增强for循环没有下标
// (2)普通for循环可以替换修改元素,增强for循环不可以替换修改元素
int[] arr = {10, 20, 30, 40, 50};
//普通for循环
for (int i = 0; i < arr.length; i++) {
arr[i] *= 2;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
int[] nums = {10, 20, 30, 40, 50};
//增强for循环
for (int element : nums) {
element *= 2;
}
for (int element : nums) {
System.out.print(element+" ");
}
System.out.println();
}
}
package com.atguigu.foreach;
import com.atguigu.array.Student;
public class TestObjectArrays2 {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("张三", 89);
students[1] = new Student("李四",84);
students[2] = new Student("王五",90);
//普通for循环
for (int i = 0; i < students.length; i++) {
// students[i].setScore(100);//修改原对象的属性
students[i] = new Student(students[i].getName(), 100);//替换对象
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].display());
}
System.out.println("=========================");
Student[] all = new Student[3];
all[0] = new Student("张三", 89);
all[1] = new Student("李四",84);
all[2] = new Student("王五",90);
//增强for循环
for (Student student : all) {
// student.setScore(100);//修改原对象的属性
student = new Student(student.getName(),100);//替换对象
}
for (Student student : all) {
System.out.println(student.display());
}
}
}
三、面向对象的基本特征之二:继承
3.1 什么是继承?
生活中:财产的继承、文化的传承、基因的延续
继承的含义:
- 延续:代码的复用
- 扩展:增加新的成员、功能的改写
3.2 为什么要继承?
代码的复用
类与类之间有 is-a 的关系
- Student is a Person. 学生是人的一个子类别。
- Teacher is a Person. 老师也是人的一种。
对父类进行扩展
没有继承,重复代码很多
package com.atguigu.inherited;
public class Person {
//这里故意先不私有化
public String name;
public int age;
public void eat(){
System.out.println("吃东西");
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age;
}
}
package com.atguigu.inherited;
public class Student {
//这里故意先不私有化
public String name;
public int age;
public int score;
public void eat(){
System.out.println("吃东西");
}
public void study(){
System.out.println("学习");
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age +",成绩:" + score;
}
}
package com.atguigu.inherited;
public class Teacher {
//这里故意先不私有化
public String name;
public int age;
public double salary;
public void eat(){
System.out.println("吃东西");
}
public void teach(){
System.out.println("讲课");
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age +",薪资:" + salary;
}
}
改为代码继承的方式:
3.3 如何继承?
语法格式:
【修饰符】 class 子类名 extends 父类名{
}
package com.atguigu.inherited;
public class Person {//父类
//这里故意先不私有化
public String name;
public int age;
public void eat(){
System.out.println("吃东西");
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age;
}
}
package com.atguigu.inherited;
//extends:扩展,继承
public class Student extends Person{//Student是Person的子类
//这里故意先不私有化
public int score;
public void study(){
System.out.println("学习");
}
public String getInfo(){
return super.getInfo() + ",成绩:" + score;
}
}
package com.atguigu.inherited;
public class Teacher extends Person{//Teacher是Person的子类
//这里故意先不私有化
public double salary;
public void teach(){
System.out.println("讲课");
}
public String getInfo(){
return super.getInfo() +",薪资:" + salary;
}
}
package com.atguigu.inherited;
public class TestStudentTeacher {
public static void main(String[] args) {
//创建Student类对象
Student s = new Student();
s.name = "张三";//从父类继承的name属性的声明
s.age = 23;//从父类继承的age属性的声明
s.score = 89;//子类自己声明
System.out.println(s.getInfo());
s.eat();//从父类继承的eat方法声明
s.study();//子类自己声明
//创建Teacher类的对象
Teacher t = new Teacher();
t.name = "柴老师";
t.age = 18;
t.salary = 15000;
t.eat();
t.teach();
System.out.println(t.getInfo());
}
}
3.4 继承的特点和要求
1、哪些成员会继承到子类中?
- 从可操作性的角度来说:只有非私有的属性、方法才会继承到子类中,即子类对象或子类中才能直接操作。
- 但是从事物特征的角度来说:所有属性、方法都会继承到子类中。
2、单独说构造器,构造器会继承吗?
- 构造器不能直接被继承。父类定义了 n 个构造器,不代表子类也有 n 个构造器。
- 但是,子类构造器
一定一定
要调用
父类的构造器- 子类构造器默认找的是父类的无参构造。如果父类没有无参构造,怎么解决?(1)给父类加无参构造(2)手动调用父类的有参构造
super()
; 这句代码通常会被省略,调用父类的无参构造super(参数)
; 这句代码用于调用父类的有参构造,这个不能省略。super()
或super(参数)
代码必须在子类构造器的首行。
为什么子类构造器中要调用父类的构造器?
构造器的作用是在 new 对象时,为对象的属性初始化。
从事物特征的角度来说,子类会从父类继承所有的属性,那么这些属性都要初始化。可以通过父类已经定义好的构造器,直接为他们初始化,不需要重复写一遍了。
3、Java 中的继承有单继承的限制。
比喻:只有 1 个亲生父亲
C++语言中有多重继承。Java 不能多重继承。
4、Java 的类允许多层继承
比喻:遵循代代相传
如何查看类的继承关系树?选择一个类名,按快捷键 Ctrl + H
5、Java 类的根父类是 Object
如果一个类没有写它的父类是谁,默认它的父类就是 Object
6、一个父类可以同时有多个子类
示例代码 1:
package com.atguigu.inherited;
public class Graphic {//图形
//定义一个私有的属性:name
private String name;
public Graphic() {
System.out.println("Graphic的无参构造");
}
public Graphic(String name) {
this.name = name;
System.out.println("Graphic的有参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo(){
return "图形名称:" + name;
}
}
package com.atguigu.inherited;
public class Rectangle extends Graphic{//矩形
private double length;//长
private double width;//宽
//构造器
public Rectangle() {
// 这里默认调用父类Graphic的无参构造。省略了super();
}
public Rectangle(String name, double length, double width) {
//如果没写 super(name); 默认也找父类的无参构造
super(name);
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public String getInfo(){
// return "矩形名称:" + name ;//父类的name是private修饰,子类不能直接使用
return "矩形名称:" + getName() +",长:" + length +",宽:" + width;
//间接使用父类声明的name属性
}
}
示例代码 2:
package com.atguigu.inherited;
public class Father {
public int money;
}
package com.atguigu.inherited;
public class Person extends Father{//父类
//这里故意先不私有化
public String name;
public int age;
//私有化
private char gender;//性别
//构造器
//无参构造
public Person() {
}
//有参构造
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public void eat(){
System.out.println("吃东西");
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age +",性别:" + gender;
}
}
package com.atguigu.inherited;
//extends:扩展,继承
public class Student extends Person{//Student是Person的子类
//这里故意先不私有化
public int score;
//子类的无参构造
public Student(){
super();//调用父类的无参构造。这句代码可以省略。
}
//子类的有参构造
public Student(String name, int age, char gender, int score) {
super(name, age, gender);//调用父类的有参构造
this.score = score;
}
public void study(){
System.out.println("学习");
}
public String getInfo(){
return super.getInfo() + ",成绩:" + score;
// return "姓名:" + name +",年龄:" + age + ",性别 :" + gender +",成绩:" + score;//错误,因为gender在父类是私有的
// return "姓名:" + name +",年龄:" + age + ",性别 :" + getGender() +",成绩:" + score;//通过getGender()间接访问
}
}
package com.atguigu.inherited;
public class Teacher extends Person{//Teacher是Person的子类
//这里故意先不私有化
public double salary;
//子类的无参构造
public Teacher() {
super();//调用父类的无参构造
}
//子类的有参构造
public Teacher(String name, int age, char gender, double salary) {
super(name, age, gender);//调用父类的有参构造
this.salary = salary;
}
public void teach(){
System.out.println("讲课");
}
public String getInfo(){
return super.getInfo() +",薪资:" + salary;
}
}
package com.atguigu.inherited;
public class TestStudentTeacher {
public static void main(String[] args) {
//创建Student类对象
Student s = new Student();
s.name = "张三";//从父类继承的name属性的声明
s.age = 23;//从父类继承的age属性的声明
s.score = 89;//子类自己声明
// s.gender = '男';//不能直接操作,因为private仅限于本类内部操作
s.setGender('男');//可以间接操作gender
s.eat();//从父类继承的eat方法声明
s.study();//子类自己声明
System.out.println(s.getInfo());//子类改写了getInfo()方法
System.out.println("=============================");
//创建Teacher类的对象
Teacher t = new Teacher();
t.name = "柴老师";//从父类继承的name属性的声明
t.age = 18;//从父类继承的age属性的声明
t.salary = 15000;//子类自己声明
t.eat();//从父类继承的eat方法声明
t.teach();//子类自己声明
System.out.println(t.getInfo());//子类改写了getInfo()方法
System.out.println("=============================");
s.money = 2000000;//从爷爷类Father继承的money属性的声明
t.money = 2000000;//从爷爷类Father继承的money属性的声明
}
}
3.5 方法的重写
当子类继承了父类的某个方法后,觉得父类方法的方法体实现不适用于子类,子类对其进行重新实现,称为重写,即在子类中覆盖父类的方法体。在重写过程中,想要复用父类被重写方法的方法体,可以通过 super.父类方法()的方式。
方法的重载(Overload) | 方法的重写(Override) | |
---|---|---|
位置 | 同一个类中 或 父子类中 | 父子类 |
方法名 | 相同 | 相同 |
形参列表 | 不同 (形参的个数、类型、顺序等不同) | 相同 |
返回值类型 | 无关 | (1)如果是基本数据类型或 void:相同 (2)如果是引用数据类型,需要满足 <=的关系 |
权限修饰符(public 等) | 无关 | 必须满足 >= 的关系,子类方法权限修饰符的可见性范围要比父类大 |
问题:父类大还是子类大?
这里的大小是从这个类代表的事物的范围角度来说的。
父类是 Person,代表是人类
子类是 Student,代表的是学生类
人的范围 > 学生的范围
示例代码 1
package com.atguigu.override;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = 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;
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age;
}
}
package com.atguigu.override;
public class Student extends Person{
private int score;
public Student() {
}
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//重新父类方法的快捷键
//(1)Ctrl + O(推荐)
//(2)或Alt + Insert 再选Override Methods
/*
重写方法的上面标记 @Override的作用?
(1)可读性更好:人家一看就知道这是一个重写的方法
(2)可以让编译器对它进行更加严格的格式校验,如果不符合重写的要求,直接报错。
但是,如果一个方法并未违反重写的要求,那么加@Override 或不加 不影响它是重写的本质。
*/
@Override
public String getInfo() {
// return "姓名:" + name +",年龄:" + age +",成绩:" + score;
//私有的子类不能直接用
//return "姓名:" + getName() +",年龄:" + getAge() +",成绩:" + score;
// return getInfo()+",成绩:" + score;//错误,这是无条件递归调用
return super.getInfo() +",成绩:" + score;
//super.getInfo()等价于 父类getInfo()方法的方法体
//即等价于 return "姓名:" + name +",年龄:" + age;
}
}
package com.atguigu.override;
public class TestStudent {
public static void main(String[] args) {
Student s = new Student("张三",23,100);
System.out.println(s.getInfo());
}
}
示例代码 2
package com.atguigu.override;
public class Base {//父类,又称为基类
public void m1(){
//....不关心方法体
}
public double m2(){
return 0.0;
}
public Object m3(){
return null;
}
protected void m4(){
}
public void print1n(){
}
}
package com.atguigu.override;
public class Sub extends Base{
public void m1(){ //重写时:返回值类型必须是void
//....不关心方法体
}
public double m2(){//重写时:返回值类型必须是double
return 0.0;
}
//Object > String
public String m3(){//重写时:返回值类型可以是Object或Object的子类
return null;
}
public void m4(){//重写时:权限修饰符可以是protected或public
}
/* @Override
public void println(){ //方法名不一样,不是重写
}*/
}
3.6 Object 类:根父类
所有类都是 Object 的子类。Object 类的所有方法,子类都会继承。
Object 类中一共有 11 个方法,咱们今天只学习 1 个,public String toString()
,重写 toString 快捷键建议用 Alt + Insert
如果我们用
System.out.println()
或System.out.print()
打印一个对象,会自动调用对象的 toString 方法。如果我们把对象与 字符串进行拼接,也会自动调用对象的 toString 方法。
建议所有 Object 的子类都重写这个方法。
查看某个类的源代码:Ctrl + N,搜索这个类
查看某个类的成员列表:Ctrl + F12,部分同学需要按 Fn
package com.atguigu.object;
public class Person {
//属性私有化
private String name;
private int age;
//无参构造
public Person() {
}
//有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//get/set
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;
}
//重写toString,
// 快捷键(1)Alt + Insert,选toString (2)Ctrl + O
/*
以下代码是Ctrl + O生成的代码模板
@Override
public String toString() {
return super.toString();
}
*/
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.atguigu.object;
public class Student extends Person{
//实例变量的属性私有化
private int score;
//无参构造
public Student() {
}
//有参构造
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
//get/set
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//重写toString
@Override
public String toString() {
return "Student{" +
super.toString()+
"score=" + score +
"} " ;
}
}
package com.atguigu.object;
public class TestStudent {
public static void main(String[] args) {
Student s = new Student("张三",23,96);
System.out.println(s);
//当我们打印对象时,JVM会自动帮我们调用toString方法
//如果我们的类没有重写toString,就会找Object的toString方法
//如果没有重写toString,打印结果是:com.atguigu.object.Student@41629346
/*
Object的toString的源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
getClass().getName():获取类的名字
hashCode():获取对象的哈希值,比喻,对象的哈希值就像对象的身份证号码,就是一个编号。
Integer.toHexString(hashCode()):把哈希值用十六进制形式显示出来
*/
System.out.println(s.hashCode());//1096979270 十进制
//对应的十六进制:41629346
}
}
四、关键字
4.1 final 关键字
final 代表最终的。
final 在 Java 中是一个修饰符,可以用来修饰类、成员变量、成员方法、局部变量。不能修饰构造器。
(1)修饰类:这个类就是一个太监类。不能有子类。例如:String、System、Math 类都是 final,它们非常重要,功能要明确的,稳定的,不允许别人继承,对其进行改写、扩展等。
(2)修改方法:这个方法不允许被重写。
(3)修饰局部变量:这个变量的值不能被修改,它是固定的常量。
(4)修饰成员变量:这个成员变量的值不能被修改,它必须显式初始化,它们不能使用默认值。
- 如果是静态的成员变量加 final,一般写成大写的
- 如果是非静态的成员变量加 final,必须在构造器中初始化,习惯上用有参构造为他们初始化,如果有无参构造,必须手动给它们指定值。
- final 修饰的成员变量,不管静态非静态,都没有 set 方法。
package com.atguigu.keyword;
public class TestVariable {
public static void main(String[] args) {
int a = 1;
a = 2;
final int b = 1;
// b = 2;//不允许重新赋值,修改它的值
}
}
package com.atguigu.keyword;
public class Triangle {//这种写法,语法没问题,但是没什么意义
private final int a = 1;
private final int b = 1;
private final int c = 1;
public Triangle() {
}
/* public Triangle(int a,int b,int c) {//此时不能写有参构造
this.a = a;
this.b = b;
this.c = c;
}*/
@Override
public String toString() {
return "Triangle{" +
"a=" + a +
", b=" + b +
", c=" + c +
'}';
}
}
package com.atguigu.keyword;
public class Triangle {
private static final String NAME = "三角形";
private final int a ;
private final int b;
private final int c ;
public Triangle(){//此时一般不写无参构造,如果要写,必须手动指定a,b,c的值
a = 1;
b = 1;
c = 1;
}
public Triangle(int a,int b,int c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public String toString() {
return "Triangle{" +
"a=" + a +
", b=" + b +
", c=" + c +
'}';
}
}
package com.atguigu.keyword;
public class TestTriangle {
public static void main(String[] args) {
Triangle t1 = new Triangle(3,4,5);
Triangle t2 = new Triangle(6,6,6);
System.out.println(t1);
System.out.println(t2);
System.out.println(Math.PI);
System.out.println(Integer.MAX_VALUE);
Triangle t3 = new Triangle();
}
}
4.2 super 关键字
super:代表父类的
前提条件:父类的构造器、方法、成员变量,都必须是非 private
它有 3 种用法:
在子类构造器首行:
super()
; 代表调用父类的无参构造(默认是它)super(参数)
; 代表调用父类的有参构造
调用父类的方法
- super.方法:当子类重写了父类的某个方法,在子类中又想要调用父类被重写的方法时,需要加 super.。如果子类并未重写,那么可以省略 super.
调用父类的成员变量
- super.成员变量:当子类定义了与父类同名的成员变量时,在子类中想要使用父类的同名的成员变量,需要加 super.。如果没有重名问题,那么可以省略 super.
访问变量时:
(1)不加 this 和 super,从局部开始找
(2)加 this.,不从局部找,从本类的成员变量开始找,找到就停,没找到去父类
(3)加 super.,不从局部和本类找,直接从父类开始找
示例代码 1
package com.atguigu.keyword;
public class Base {
int a = 1;
int b = 1;
public void method(){
System.out.println("父类Base的method方法");
}
}
package com.atguigu.keyword;
public class Sub extends Base{
int a = 2;
int c = 2;
public void printA(){
System.out.println("a = " + a);
System.out.println("super.a = " + super.a);
}
public void showA(int a){
System.out.println("a = " + a);//先找局部的
System.out.println("this.a = " + this.a);//局部变量与成员变量重名时,在成员变量前面加this.
System.out.println("super.a = " + super.a);//子类成员变量与父类成员变量重名是,在父类的成员变量前面加super.
}
public void printB(){
System.out.println("b = " + b);//先找局部的,找不到再从本类成员变量找,再找不到找父类的
System.out.println("b = " + this.b);//先找本类成员变量找,找不到找父类的
System.out.println("super.b = " + super.b);//直接找父类的
}
public void printC(){
System.out.println("c = " + c);
System.out.println("c = " + this.c);
// System.out.println("super.c = " + super.c);//错误,因为c不是父类的
}
public void method(){
super.method();
}
}
package com.atguigu.keyword;
public class TestSub {
public static void main(String[] args) {
Sub sub = new Sub();
// sub.method();
// sub.printA();
sub.printB();
// sub.showA(3);
}
}