Java 面向对象编程基础 - 学习笔记
模块二 面向对象
任务一: 类和对象
第一讲:对象和面向对象的概念
第二讲:面向对象编程的概念
第三讲:类和对象的概念
第四讲:类和对象以及引用的定义
<1>类的定义
class 类名{
类体;
}
<2>成员变量的定义
class 类名{
数据类型 成员变量名 = 初始值;
}
<3>对象的创建
new 类名();
// 这个过程叫做类的实例化
// 创建对象的本质: 在内存空间的堆区申请一块内存区域,用于存放该对象独有特征信息。
<4>引用的定义
a. 使用引用数据类型定义的变量叫做引用型变量,简称为 引用
b. 引用变量主要用于记录对象在堆区中的内存地址信息,便于下次访问
c. 语法格式
类名 引用变量名;
引用变量名.成员变量名;
第五讲 - 第六讲:Person类的定义
public class Person{
//数据类型 成员变量名 = 初始值; 其中 = 初始值 通常都省略不写
String name;
int age;
public static void main(String[] args){
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 打印对象中的成员变量值
// 引用变量名.成员变量名
. System.out.println("我是" + p,name + ", 今年 "+ p.age + "岁了!") // null 0
// 修改成员变量的数值
p.name = "John Lennon";
p.age = 100;
}
}
第七讲:Point类
public class Point{
int x;
int y;
public static void main(String[] args){
Point p = new Point();
p.x = 1;
p.y = 2;
System.out.println("x = "+p.x+", y = " + p.y);
}
}
第八讲:成员方法
<1>定义
class 类名{
返回值类型 成员方法名(形参列表){
成员方法体;
}
}
第九讲 - 第十讲: Person 类中无参无返回值的成员方法
public class Person{
//数据类型 成员变量名 = 初始值; 其中 = 初始值 通常都省略不写
String name;
int age;
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加 // 引用.的前缀
void show(){
System.out.println("name = " + name +", age = "+age);
}
public static void main(String[] args){
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 修改成员变量的数值
p.name = " John Lennon";
p.age = 100;
// 打印对象中的成员变量值
// 打印方法的调用: 引用变量名.成员方法名(实参列表);
// 实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个数,类型
// 以及顺序要完全一致
p.show();
}
}
第十一讲:Point类中无参无返回值成员方法的定义
public class Point{
int x;
int y;
// 自定义成员方法,实现成员变量数值的打印
void show(){
System.out.println("x = "+x+", y = " + y);
}
public static void main(String[] args){
Point p = new Point();
p.x = 1;
p.y = 2;
}
}
第十二讲 - 第十三讲:Person类中有参数值,无返回值的成员方法
public class Person{
//数据类型 成员变量名 = 初始值; 其中 = 初始值 通常都省略不写
String name;
int age;
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加 // 引用.的前缀
void show(){
System.out.println("name = " + name +", age = "+age);
}
// 自定义成员方法,实现将姓名修改为 指定数值
void setName(String name){
this.name = name;
}
// 自定义成员方法,实现将年龄修改为 指定数值
void setAge(int age){
this.age = age;
}
// 自定义成员方法,修改姓名和年龄(不推荐使用,因为降低了功能之间的耦合性)
void setNameAndAge(String name, int age){
this.name = name;
this.age = age;
}
public static void main(String[] args){
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 修改成员变量的数值
p.name = " John Lennon";
p.age = 100;
p.setName("Joe Biden");
p.setNameAndAge("Obama", 50);
// 打印对象中的成员变量值
// 打印方法的调用: 引用变量名.成员方法名(实参列表);
// 实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个数,类型
// 以及顺序要完全一致
p.show();
}
}
第十四讲:Point类有参无返回值成员方法
public class Point{
int x;
int y;
void setX(int x){
this.x = x;
}
void setY(int y){
this.y = y;
}
// 自定义成员方法,实现成员变量数值的打印
void show(){
System.out.println("x = "+x+", y = " + y);
}
public static void main(String[] args){
Point p = new Point();
p.x = 1;
p.y = 2;
p.setX(10);
p.setY(20);
p.show();
}
}
第十五讲:Person类中,可变长参数 的使用
<1>语法规则
返回值类型 方法名(参数的类型... 参数名)
参数个数是可以改变的,也就是0~n个
一个方法的参数列表中最多只能声明一个可变长参数,并且需要放在末尾
<2>代码
public class Person{
//数据类型 成员变量名 = 初始值; 其中 = 初始值 通常都省略不写
String name;
int age;
// 可变长参数,可看做 一维数组 使用即可
void showArguments(String... args){
for(int i = 0; i< args.length; i++){
System.out.println( "args[" +i+ "] =" + args[i] );
}
}
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加 // 引用.的前缀
void show(){
System.out.println("name = " + name +", age = "+age);
}
// 自定义成员方法,实现将姓名修改为 指定数值
void setName(String name){
this.name = name;
}
// 自定义成员方法,实现将年龄修改为 指定数值
void setAge(int age){
this.age = age;
}
// 自定义成员方法,修改姓名和年龄(不推荐使用,因为降低了功能之间的耦合性)
void setNameAndAge(String name, int age){
this.name = name;
this.age = age;
}
public static void main(String[] args){
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 修改成员变量的数值
p.name = " John Lennon";
p.age = 100;
p.setName("Joe Biden");
p.setNameAndAge("Obama", 50);
// 打印对象中的成员变量值
// 打印方法的调用: 引用变量名.成员方法名(实参列表);
// 实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个数,类型
// 以及顺序要完全一致
p.show();
p.showArguments("大","家","好","才","是","真","的","好");
}
}
第十六讲:Point类中可变长参数的使用
public class Point{
int x;
int y;
void showArguments(int... args){
for(int i = 0; i< args.length; i++){
System.out.println( "args[" +i+ "] =" + args[i] );
}
}
void setX(int x){
this.x = x;
}
void setY(int y){
this.y = y;
}
// 自定义成员方法,实现成员变量数值的打印
void show(){
System.out.println("x = "+x+", y = " + y);
}
public static void main(String[] args){
Point p = new Point();
p.x = 1;
p.y = 2;
p.setX(10);
p.setY(20);
p.showArguments(1,2,3,4,5,6,7);
}
}
第十七讲:Person类中 无参 有返回值 的方法 (get方法)
第十八讲:Point类中 无参 有返回值 的方法 (get方法)
第十九讲:方法的传参过程
第二十讲 - 第二十二讲: 参数传递的注意事项
/* 编程实现参数传递的测试 */ public class ArgumentTest { // 自定义成员方法打印参数传入的整数数据 // int ia = ib = 10; void show1(int ia) { ia = 200; System.out.println("show方法中:ia = " + ia); // 10 200 } // 自定义成员方法打印参数传入的数组内容 void show2(int[] arr1) { arr1 = new int[2]; // 加上改行代码后,相当于在堆区中又重新申请一块内存空间 arr1[0] = 200; System.out.println("show方法中:arr1[0] = " + arr1[0]); // 10 200 200 } public static void main(String[] args) { // 1.声明ArgumentTest类型的引用指向该类型的对象 ArgumentTest at = new ArgumentTest(); // 2.使用引用变量调用show1方法进行测试 int ib = 10; at.show1(ib); System.out.println("main方法中:ib = " + ib); // ib = 10 System.out.println("-----------------------------------"); // 3.调用show2方法进行测试 int[] arr2 = new int[]{10, 20}; at.show2(arr2); System.out.println("main方法中:arr2[0] = " + arr2[0]); // 10 200 10 } }
第二十三讲:任务总结
1.面向对象编程的概念(理解)
对象,面向对象,面向对象编程
-
类和对象以及引用(重中之重)
类和对象,类的定义,成员变量的定义,对象的创建,引用的定义等
-
成员方法(重中之重)
语法格式,调用格式,传参的过程等
任务二: 方法和封装
class类名{
类名 (形参列表){
方法体;
}
}
第二讲:构造方法的作用
第三讲:Point类的定义
public class Point{
int x;
int y;
// 自定义无参构造方法
Point() {}
// 自定义有参构造方法
Point(int i, int j){
x = i;
y = j;
}
public static void main(String[] args){
Point p1 = new Point();
Point p2 = new Point(100,200);
}
}
第四讲:重载的概念和体现形式
<1> 重载
方法名相同,参数列表不同,这样的方法之间构成重载关系(Overload)。
<2> 代码
/* 编程实现方法重载主要形式的测试 */ public class OverloadTest { // 自定义成员方法 void show() { System.out.println("show()"); } void show(int i) { // ok 体现在方法参数的个数不同 System.out.println("show(int)"); } void show(int i, double d) { // ok 体现在方法参数的个数不同 System.out.println("show(int, double)"); } void show(int i, int j) { // ok 体现在方法参数的类型不同 System.out.println("show(int, int)"); } void show(double d, int i) { // ok 体现在方法参数的顺序不同 System.out.println("show(double, int)"); } /* void show(double a, int b) { // error 与参数变量名无关 System.out.println("show(double, int)"); } */ /* int show(double d, int i) { // error, 与返回值类型无关 System.out.println("show(double, int)"); } */ public static void main(String[] args) { // 1.声明OverloadTest类型的引用指向该类型的对象 OverloadTest ot = new OverloadTest(); // 2.调用show方法 ot.show(); ot.show(66); ot.show(66, 3.14); ot.show(66, 118); ot.show(3.14, 118); //ot.show(3.14, 66); } }
第五讲:Person类中重载的使用
void grow(){
age++;
}
void grow(int i){
age += i;
}
第六讲:Point类中重载的使用
第七讲:重载的实际意义
调用者只需要记住一个方法名,就可以调用不同的方法,实现不同的功能
第八讲 - 第十二讲:this关键字
<1> 在成员变量前加上 this. 的前缀,明确了该变量是成员变量 (重点)
<2> 可以通过this. 的方式调用成员变量和成员方法 (重点)
<3> this可以作为方法的返回值 (重点)
例子:
// 自定义成员方法,实现 Person类型对象 的获取 并返回
Person getPerson(){
return this;
}
// 调用
Person p2 = p1.getPerson();
<4> 在构造方法的第一行,可以使用this()的方式来调用 本类中的其他构造方法 (了解)
public class Boy { String name; // 用于描述姓名的成员变量 // 自定义构造方法 Boy() { // 调用本类中的有参构造方法 //this("无名"); System.out.println("无参构造方法!"); } Boy(String name) { // 调用本类中的无参构造方法 this(); System.out.println("=========有参构造方法!"); this.name = name; } // 自定义成员方法实现特征的打印 void show() { System.out.println("我的名字是:" + name); } public static void main(String[] args) { // 1.使用无参方式构造对象并打印特征 Boy b1 = new Boy(); b1.show(); // null System.out.println("-----------------------------------"); // 2.使用有参方式构造对象并打印特征 Boy b2 = new Boy("张飞"); b2.show(); // 张飞 } }
第十四讲 - 第十六讲: 递归 - 阶乘
/* 编程实现累乘积的计算并打印 */ public class FacTest { // 自定义成员方法实现将参数n的阶乘计算出来并返回 // 1! = 1; 2! = 1*2; 3! = 1*2*3; ... n! = 1*2*3*...*n; int show(int n) { // int n=5; int n = 4; int n = 3; int n = 2; int n = 1; // 递推的方式 /* int num = 1; for(int i = 1; i <= n; i++) { num *= i; } return num; */ /* 5! = 5 * 4 * 3 * 2 * 1; 4! = 4 * 3 * 2 * 1; 3! = 3 * 2 * 1; 2! = 2 * 1; 1! = 1; 5! = 5 * 4!; 4! = 4 * 3!; 3! = 3 * 2!; 2! = 2 * 1!; 1! = 1; n! = n * (n-1)!; */ // 递归的方式 // 当n的数值为1时,则阶乘的结果就是1 /* if(1 == n) { return 1; } */ if(1 == n) return 1; // 否则阶乘的结果就是 n*(n-1)! return n*show(n-1); // show(5) => return 5*show(4); => 120 // show(4) => return 4*show(3); => 24 // show(3) => return 3*show(2); => 6 // show(2) => return 2*show(1); => 2 // show(1) => return 1; => 1 } public static void main(String[] args) { // 1.声明FacTest类型的引用指向该类型的对象 FacTest fct = new FacTest(); // 2.调用方法进行计算并打印 int res = fct.show(5); System.out.println("最终的计算结果是:" + res); // 120 } }
第十七讲: 斐波那契数列 - 递归法
/* 编程实现费氏数列的计算并打印 功能类/封装类 */ public class Fee { // 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定 // 1 1 2 3 5 8 13 21 .... int show(int n) { // int n = 5; int n = 4; int n = 3; int n = 2; int n = 1; // 1.使用递归的方式进行计算 // 当n=1或者n=2时,结果是1 if(1 == n || 2 == n) { return 1; } // 否则结果是前两项的和 return show(n-1) + show(n-2); // show(5) => return show(4) + show(3); => 5 // show(4) => return show(3) + show(2); => 3 // show(3) => return show(2) + show(1); => 2 // show(2) => return 1; => 1 // show(1) => return 1; => 1 } }
第十八讲: 斐波那契数列 - 递推法
// 使用递推的方式进行计算 int show(int n) { int ia = 1; int ib = 1; for(int i = 3; i <= n; i++) { int ic = ia + ib; ia = ib; ib = ic; } return ib; }
第十九讲:代码的拆分实现
第二十讲 - 第二十二讲:封装
/* 编程实现Student类的封装 封装类 */ public class Student { // 1.私有化成员变量,使用private关键字修饰 // private关键字修饰表示私有的含义,也就是该成员变量只能在当前类的内部使用 private int id; // 用于描述学号的成员变量 private String name; // 用于描述姓名的成员变量 // 3.在公有的构造方法中调用set方法进行合理值的判断 public Student() {} public Student(int id, String name) { //this.id = id; //this.name = name; setId(id); setName(name); } // 2.提供公有的get和set方法,并在方法体中进行合理值的判断 // 使用public关键字修饰表示公有的含义,也就是该方法可以在任意位置使用 public int getId() { return id; } public void setId(int id) { if(id > 0) { this.id = id; } else { System.out.println("学号不合理哦!!!"); } } public String getName() { return name; } public void setName(String name) { this.name = name; } // 自定义成员方法实现特征的打印 // 什么修饰符都没有叫做默认的访问权限,级别介于private和public之间 public void show() { //System.out.println("我是" + name + ",我的学号是" + id); System.out.println("我是" + getName() + ",我的学号是" + getId()); } }
/* 编程实现Student类的测试 */ public class StudentTest { public static void main(String[] args) { // 1.声明Student类型的引用指向Student类型的对象 Student s1 = new Student(); // 2.对成员变量进行赋值并打印 //s1.id = -1001; //s1.name = "张飞"; s1.setId(-1001); s1.setName("张飞"); s1.show(); // 1001 张飞 System.out.println("----------------------------------------------------"); // 3.使用有参方式构造对象并打印特征 Student s2 = new Student(-1001, "张飞"); s2.show(); } }
<1>
/* 编程实现学生信息的录入和打印 */ import java.util.Scanner; public class StudentTest2 { public static void main(String[] args) { // 1.提示用户输入学生的人数并使用变量记录 System.out.println("请输入学生的人数:"); Scanner sc = new Scanner(System.in); int num = sc.nextInt(); // 2.根据学生的人数准备对应的一维数组 // int[] arr = new int[3]; - 表示声明一个长度为3元素类型为int类型的一维数组 // 数组中的每个元素都是int类型,也就是说数组中的每个元素都可以看做是一个int类型的变量,使用整数数据进行初始化 arr[0] = 10; // 下面的代码是声明一个长度为num元素类型为Student类型的一维数组 // 数组中的每个元素都是Student类型,也就是说数组中的每个元素都可以看做Student类型的变量,arr[0] = new Student(); Student[] arr = new Student[num]; // 3.提示用户输入每个学生的信息(学号 姓名)并记录到一维数组中 for(int i = 0; i < num; i++) { System.out.println("请输入第" + (i+1) + "个学生的信息(学号 姓名):"); arr[i] = new Student(sc.nextInt(), sc.next()); } System.out.println("-----------------------------------------------"); // 4.打印所有学生信息 System.out.println("该班级的所有学生信息有:"); for(int i = 0; i < num; i++) { //System.out.println(arr[i]); arr[i].show(); } } }
<2> JavaBean的概念
JavaBean本质上就是符合以下标准的Java类:
类是公共的
有属性,且有对应的get,set方法
第二十四讲:任务总结
1.构造方法(重中之重)
语法格式,默认构造方法,实现成员变量的初始化
2.方法重载(重点)
概念,体现形式,实际意义
3.this关键字(原理)
概念,原理,使用方式
4.递归(难点)
概念,使用原则
5.封装(重中之重)
概念,实现流程
Plus: 局部变量和成员变量的区别:
1.定义的位置不一样【重点】
-
局部变量:在方法的内部
-
成员变量:在方法的外部,直接写在类当中
2.作用范围不一样【重点】
-
局部变量:只有方法当中才可以使用,出了方法就不能再用了
-
成员变量:整个类都可以通用
任务三:static 关键字和继承
第一讲:People类和测试类的实现
<1> 实现People类的封装,特征有:姓名,年龄,国籍
<2> 编程实现PeopleTest类,main方法中使用有参方式构造两个对象并打印
<3> 代码:
public class People{
private String name;
private int age;
private String country;
//构造方法
public People(){}
public People(String name, int age, String country){
setName(name);
setAge(age);
setCountry(country);
}
public void show(){
System.out.println("name = " +name+ " age = " + age + " country = "+country);
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age
}
public void setAge(int age){
if(age > 0 && age < 150){
this.age = age;
}else{
System.out.println("年龄不合理哦!!!");
}
}
public String getCountry(){
return country;
}
public void setCountry(String country){
this.country = country;
}
}
// 编程实现People类的测试
public class PeopleTest{
public static void main(String[] args){
// 1.使用有参方式构造两个People类型的对象并打印特征 People p1 = new People("zhangfei", 30, "China"); p1.show(); // zhangfei 30 China People p2 = new People("guanyu", 35, "China"); p2.show(); // guanyu 35 China
}
第二讲:static关键字的基本概念
<1> 使用static修饰后, 成员变量由 对象层级 提升为 类层级
也就是,整个类 只有一份 并 被所有对象共享,
该成员变量 随着类的加载 准备就绪,与 是否 创建对象 无关
<2>
// 3.验证static关键字修饰的静态成员(类成员)是否与创建对象无关 类名.的方式 => 无关 //System.out.println("获取到的国籍信息是:" + People.country); // null System.out.println("获取到的国籍信息是:" + People.getCountry()); // null // 2.验证static关键字修饰的静态成员(类成员) 是否被所有对象共享 => 共享 //p1.country = "蜀国"; p1.setCountry("蜀国"); //System.out.println("第一个对象的国籍是:" + p1.country); // 蜀国 //System.out.println("第二个对象的国籍是:" + p2.country); // 蜀国 System.out.println("第一个对象的国籍是:" + p1.getCountry()); // 蜀国 System.out.println("第二个对象的国籍是:" + p2.getCountry()); // 蜀国 People p3 = new People(); //System.out.println("第三个对象的国籍是:" + p3.country); // 蜀国 System.out.println("第三个对象的国籍是:" + p3.getCountry()); // 蜀国
<3> static关键字修饰的成员,可以使用 引用. 的方式访问, 但 推荐 类名. 的方式
第三讲:static关键字的使用方式
<1>
public class StaticTest { private int cnt = 1; // 隶属于对象层级,也就是每个对象都拥有独立的一份 private static int snt = 2; // 隶属于类层级,也就是所有对象都共享同一份 // 自定义非静态的成员方法 需要使用引用.的方式访问 public void show() { System.out.println("cnt = " + this.cnt); // 1 System.out.println("snt = " + this.snt); // 2 // 静态成员被所有对象共享,this关键字可以省略 } public static void main(String[] args) { StaticTest st = new StaticTest(); st.show(); } }
在 非静态成员方法中 既能访问非静态的成员,又能访问 静态的成员
(成员:成员变量+成员方法,静态成员被所有对象共享)
<2>
(这一特点,体现了Java语言的安全性)
<3>只有隶属于类层级并被所有对象共享的内容才可以使用static修饰(不能滥用static 关键词)
<4> 静态成员变量的get(),set()方法 (规范)
public class People { private static String country; public static String getCountry() { return country; } public static void setCountry(String country) { //this.country = country; People.country = country; } }
/* 编程实现构造块和静态代码块的使用 */ public class BlockTest { // 当需要在执行构造方法体之前做一些准备工作时,则将准备工作的相关代码写在构造块中即可,比如:对成员变量进行的统一初始化操作 { System.out.println("构造块!"); // (2) } // 静态代码块会随着类的加载而准备就绪,会先于构造块执行 // 当需要在执行代码块之前随着类的加载做一些准备工作时,则编写代码到静态代码块中,比如:加载数据库的驱动包等 static { System.out.println("#####################静态代码块!"); // (1) } // 自定义构造方法 public BlockTest() { System.out.println("====构造方法体!"); // (3) } public static void main(String[] args) { BlockTest bt = new BlockTest(); BlockTest bt2 = new BlockTest(); // 静态代码块执行了一次, 构造块执行了两次 } }
第五讲 main方法详解
<1> 向main(String[] args)方法中传递参数:
javac MainTest.java
第六讲 - 第八讲:Singleton 和SingletonTest类
要求:Singleton 只能被实例化一次 ==> Singleton的构造方法私有化
public class Singleton{ private static Singleton s = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return s; } } public class SingletonTest{ public static void main(String[] args){ // <1> 不符合单例的要求 //Singleton s1 = new Singleton(); //Singleton s2 = new Singleton(); //System.out.println(s1 == s2); // false // <2> 需要封装变量Singleton s的原因: // Singleton.s = null; // 可以使得引用变量无效 // <3>正确: Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); // 此时:s1 == s2 } }
图解:
第九讲:单例设计模式
<1> 对外只提供一个对象的类 ==> 单例类
<2> 实现流程
<3>应用:
Windows的任务管理器,只能打开一个
<4>单例的两种实现模式: 饿汉式 / 懒汉式
public class Singleton { // 2.声明本类类型的引用指向本类类型的对象,使用private static关键字共同修饰 //private static Singleton sin = new Singleton(); // 饿汉式 private static Singleton sin = null; // 懒汉式 // 1.私有化构造方法,使用private关键字修饰 private Singleton() {} // 3.提供公有的get方法负责将对象返回出去,使用public static关键字共同修饰 public static Singleton getInstance() { //return sin; if(null == sin) { sin = new Singleton(); } return sin; } }
<2> 使用extends表示继承关系
第十一讲:继承的意义
public class Person{ private String name; private int age; Person(){ System.out.println("Person()"); } Person(String name, int age){ System.out.println("Person(String,int)"); setName(name); setAge(age); } public String getName(){ return name; } public void setName(String name){ this.name = name; } public int getAge(){ return age; } public void setAge(int age){ if( age > 0 && age < 150){ this.age = age; } else{ System.out.println(“年龄不合理哦!”); } } public void show(){ System.out.println("name = "+ name + " age = " + age); } public void eat(String food){ System.out.println(food + "真好吃!"); } public void play(String game){ System.out.println(game +"真好玩!"); } }
// Worker public class Worker extends Person { private int salary; public Worker() { super(); // 表示调用父类的无参构造方法, 若没有加,则编译器 自动添加 System.out.println("Worker()"); } public Worker(String name, int age, int salary){ super(name,age); // 表示调用父类的有参构造方法 System.out.println("Worker(String,int,int)"); setSalary(salary); } public int getSalary(){ return salary; } public void setSalary(int salary){ if(salary >= 2200){ this.salary = salary; }else{ System.out.println("薪水不合理哦!"); } } // 自定义成员方法表述工作行为 public void work(){ System.out.println("今天的砖头有点烫手..."); } } public class Teacher extends Person { } // WorkerTest public class WorkerTest{ public static void main(String[] args){ Worker w1 = new Worker(); // 调用顺序 Person() ==> Worker() w1.show(); // 正确: null 0 // 注意: Worker类中没有 show 方法 // 说明: Worker类的引用可以调用Person类的 show 方法 Worker w2 = new Worker("张飞",30,3000); // 调用顺序 Person(String,int,int) ==> Worker(String,int,int) w2.show(); // 正确: null 0 // 注意: Worker类中没有 show 方法 // 说明: Worker类的引用可以调用Person类的 show 方法 w2.eat("豆芽"); w2.play("王者荣耀"); w2.work(); } }
继承提高了代码的复用性,可维护性和扩展性, 是多态的前提条件。
第十二讲 - 第十四讲: 继承的特点
<2> 私有成员变量 可以被继承 只是不能直接访问
<3> super()
<4> 使用继承必须满足逻辑关系: 子类 is a 父类,也就是不能滥用继承
<5> Java只支持单继承: 一个孩子只能有一个父亲, 一个父亲可以有多个孩子
第十五讲:方法的重写 (Override)
<1> 父类方法不满足子类需求 ===> 重新写一个一样的方法,将父类的版本覆盖掉
public class Person{ public void show(){ System.out.println("name = "+ name + " age = " + age); } } public class Worker extends Person { @Override // 标注/注解,用于说明下面的方法是对父类方法的重写,若没有构成重写,则编译报错 public void show(){ super.show(); //表示调用父类的show()方法 System.out.println( "salary = " + getSalary()); } }
第十六讲:方法重写的原则
<1> 要求方法名相同, 参数列表相同 以及 返回值类型 相同,
从Java5开始 允许 返回子类类型
<2> 方法的访问权限不能变小,可以相同或者变大
错误:父类 public 子类 private
正确:父类 private 子类public
可以这样理解: 子类继承父类,是一种扩展的实现
<3> 子类不能抛出更大的异常
第十八讲- 第十九讲:IDEA的安装和初始化
第二十讲 - 第二十一讲:Animal类,Dog类,DogTest类的实现
第二十二讲: 构造块与静态代码块(笔试考点)
<1> 先执行父类的静态代码块,再执行子类的静态代码块
先执行父类的构造块,执行父类的构造方法体
先执行子类的构造块,执行子类的构造方法体
<2> 例子:
package com.lagou.task08; public class SuperTest { { System.out.println("SuperTest类中的构造快!"); } static { System.out.println("SuperTest类中的静态代码块!"); } public SuperTest(){ System.out.println("SuperTest中的构造方法体!"); } } package com.lagou.task08; public class SubSuperTest extends SuperTest { { System.out.println("========= SubSuperTest类中的构造快!"); } static { System.out.println("========= SubSuperTest类中的静态代码块!"); } public SubSuperTest(){ System.out.println("========= SubSuperTest类中的构造方法体!"); } public static void main(String[] args) { // 使用无参方式构造子类的对象 SubSuperTest sst = new SubSuperTest(); } } 运行结果: SuperTest类中的静态代码块! ========= SubSuperTest类中的静态代码块! SuperTest类中的构造快! SuperTest中的构造方法体! ========= SubSuperTest类中的构造快! ========= SubSuperTest类中的构造方法体!
二十三讲:权限修饰符和包的定义
<2> private成员只能在本类内部使用
成员方法都用public关键字修饰,成员变量都使用private关键词修饰
<3> package语句的由来
为了实现项目管理,解决命名冲突,以及权限控制的效果
在指定包名的时候,也按照一定的规范
<4>import
-
导入包
-
导入类中的成员: import static java.lang.System.out; (很少使用)
第二十四讲:final修饰类和方法的作用
<1> final 修饰类 ==> 该类不能被继承
--- 主要用于防止滥用继承,如: java.lang.String类 等
<2> final修饰方法 ==> 方法不能被重写,但可以被继承
--- 主要用于防止不经意间造成重写,如: java.text.Dateformat类中format方法等
<3>
多行注释快捷键: ctrl + shift + /
单行注释快捷键: ctrl + /
第二十五讲: final修饰成员变量的作用
<1> final修饰成员变量 ==> 该变量必须初始化,且不能改变
--- 主要用于防止不经意间造成改变,如: java.lang.Thread类中MAX_PRIORITY等
<2> final变量的3 种初始化方式
package com.lagou.task08; public class FinalMemberTest { // private final int cnt = 1; // 显式初始化 private final int cnt; /*{ cnt = 2; // 构造块中进行初始化 }*/ public FinalMemberTest() { cnt = 3; // 构造方法体中进行初始化 } public static void main(String[] args) { // 声明FinalMemberTest类型的引用指向该类的对象 FinalMemberTest fmt = new FinalMemberTest(); // 打印成员变量的数值 System.out.println("fmt.cnt = " + fmt.cnt); // 0 1 2 3 } }
第二十六讲:任务总结
1.static关键字
概念使用方式构造块 和 静态代码块单例设计模式(饿汉式和懒汉式)等
2.继承
概念特点方法方法的重写重写的原则IDEA的使用等
3.访问控制
publicprivatepackage导入等
4.final关键字
概念修饰类修饰成员方法修饰成员变量常量的概念等
任务四:多态和特殊类
第一讲:多态的概念和语法格式
<1>多态的概念
多态主要指 同一事物 表现出来的 多种形态
饮料:可乐,雪碧,红牛...
人:学生,教师,工人,保安...
图形:矩形,圆形,梯形...
<2>多态的语法格式
父类类型 引用 = new 子类类型(); // 父类引用指向子类对象
如: Shape s = new Rect();
s.show();
第二讲 - 第三讲:Shape类和Rect类
<0> 快捷键 ctrl + d : 复制并粘贴当前行
快捷键alt + shift + 上/下方向键: 移动当前行
<1> 代码
package com.lagou.class09; public class ShapeRectTest { public static void main(String[] args) { // 父类 ===> 父类 Shape s = new Shape(1,2); s.show(); // 1 2 // 子类 ===> 子类 Rect r = new Rect(3,4,5,6); r.show(); // 3 4 (子类中没有show方法) // 父类 ===> 子类 Shape s2 = new Rect(7,8,9,10); s2.show(); // 7 8 (子类中没有show方法) } }
<2> 代码
// 父类 ===> 父类 Shape s = new Shape(1,2); s.show(); // 1 2 // 子类 ===> 子类 Rect r = new Rect(3,4,5,6); r.show(); // 3 4 5 6 (子类中有show方法) // 父类 ===> 子类 // 披着 "羊皮"(Shape) 的 ”狼“(Rect) Shape s2 = new Rect(7,8,9,10); s2.show(); // 7 8 9 10 (子类中有show方法)
第四讲:多态的特点
Shape s2 = new Rect(7,8,9,10);
int X = s2.getX(); // 1. 可以直接调用 父类独有的方法
// 2. 不能直接调用 子类独有的方法
<2> 父子类都有的 非静态 方法, 编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)
<3>父子类都有的 静态 方法, 编译阶段 & 运行阶段 都调用 父类版本
第五讲:引用数据类型之间转换的方式
<1> 使用父类类型的引用,调用子类独有方法 的方式
Shape s2 = new Rect(7,8,9,10);
( (Rect) sr ).getLen(); // Shape 父大 ===> Rect 子小 (向下转型,显示类型扎转换)
<2> 自动类型转换: 子类 ===> 父类 (向上转型, 小 ===> 大)
例子:
Shape s2 = new Rect(7,8,9,10);
第六讲:引用数据类型 转换 的 注意事项
<1>
Shape s2 = new Rect(7,8,9,10);
Circle c1 = (Circle) s2; // 运行报错:Rect 不能转换为 Circle (ClassCastException)
// 羊皮 (Shape) 揭掉变成狼 (Rect)
<2>
为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if (引用变量 instanceof 数据类型){ // 判断引用变量 指向的 对象 是否为 后面的 数据类型
}
<3> 例子
if (s2 instanceof Circle){
System.out.println("可以放心地转换了");
Circle c1 = (Circle) s2;
}else{
System.out.println("强转有风险,操作需谨慎!");
}
第八讲:多态的实际意义
<1>
<2>
<3> 解析
// 自定义成员方法实现既能打印矩形对象又能打印圆形对象的特征,对象由参数传入 子类 is a 父类// Shape s = new Rect(1, 2, 3, 4); 父类类型的引用指向子类类型的对象,形成了多态// Shape s = new Circle(5, 6, 7); 多态// 多态的使用场合一:通过参数传递形成了多态public static void draw(Shape s) { // 编译阶段调用父类的版本,运行阶段调用子类重写以后的版本 s.show();}
第九讲:抽象方法 和 抽象类 的概念
<1>抽象方法: 不能具体实现的方法,使用abstract关键字修饰,并且没有方法体
<2> 语法格式
访问权限 abstract 返回值类型 方法名(形参列表);
<3> 例子
public abstract void cry();
"方法体没法写,但这个方法还得存在" ===> 写成抽象方法
<4> 抽象类: 不能具体实例化的类,也就是不能创建对象,使用abstract关键字修饰
<5>例子
package com.lagou.task09; public abstract class AbstractTest { private int cnt; public AbstractTest() { } public AbstractTest(int cnt) { setCnt(cnt); } public int getCnt() { return cnt; } public void setCnt(int cnt) { this.cnt = cnt; } // 自定义抽象方法 public abstract void show(); public static void main(String[] args) { // 错误: // 声明该类类型的引用指向该类类型的对象 //AbstractTest at = new AbstractTest(); } }
<7> 抽象类 应该(不是必须) 具有 抽象方法
第十讲:抽象类的实际意义
<1>抽象类的实际意义,不在于自身创建对象,而在于,被继承
<2>例子
public abstract class AbstractTest {
// 自定义抽象方法 public abstract void show();
}
public class SubAbstractTest extends AbstractTest { @Override public void show() { System.out.println("其实我是被迫重写的,否则我也得变成抽象的呀!"); }
<3> 继承抽象类,则必须重写 抽象方法, 否则, 该类也得是 抽象类。
<4> 抽象类对子类具有强制性和规范性, 因此 叫做 模板设计 模式
第十一讲: 多态在抽象类中的应用
<1> 多态的另一种形式:
抽象类的引用 指向 子类类型的对象
<2>例子:
// 方式一:
SubAbstractTest s = new SubAbstractTest();
AbstractTest a = new SubAbstractTest();
// 方式二(多态):AbstractTest 是抽象类,SubAbstractTest是抽象类的实现类
第十二讲:抽象类的应用
<1>例子
银行有 定期账户(Saving Account) 和 活期账户(Checking Account)。
继承自 账户类。
账户类中:
public abstract class Account{
private double money;
public abstract double getInterest();
}
<2> 代码
Account.java
FixedAccount.java
第十三讲:笔试考点
// private 和 abstract 关键字不能共同修饰一个方法// 错误:private abstract double getLixi();
// final 和 abstract 关键字不能共同修饰一个方法// 错误:public final abstract double getLixi();
// static 和 abstract 关键字不能共同修饰一个方法// 错误:public static abstract double getLixi();
第十四讲:接口的基本概念
<1>
接口是一种 比 抽象类 还 抽象的类,体现在 所有方法 都为 抽象方法
<2>
定义接口的关键字是: interface
<3>
代码
public interface InterfaceTest{
/ * public static final 可以省略*/ int CNT = 1; // 里面只能有常量
// private void show(){}// 从JAVA9开始允许接口中出现私有方法
/ * public abstract 可以省略 */ void show(); //里面只能有抽象方法 (新特性除外)
// 注释中的关键字可以省略,但建议写上
}
第十五讲:接口的实际意义
<1> 定义类的关键字是class,定义接口的关键字是interface,如:
金属接口,货币接口,黄金类
<2> 支持 “继承”多个接口
黄金类 实现 货币接口
黄金类 实现 金属接口
<3> 代码
第十六讲 - 第十七讲:类和接口之间的关系 - 抽象类和接口的主要区别
<1> Runner 接口 ------ Hunter接口 extends Runner接口 --- Man类 implements Hunter
只要涉及到接口,都支持: 多继承,多实现
接口和接口直接:多继承
接口和类之间:多实现
弥补了JAVA中不支持多继承的不足
<2> 抽象类 和 接口 区别
-
抽象类关键字: abstract class , 接口关键字: interface
-
继承抽象类: extends, 实现接口:implements
-
抽象类:可以有构造方法, 接口:不可以
-
抽象类:可以有成员变量, 接口:不可以
-
抽象类:可以有成员方法, 接口:只能有抽象方法 (JAVA8以前)
-
在接口中,增加方法时,类一定需要重写(JAVA8 以前)
-
接口中,允许出现非抽象方法 和 静态方法, 非抽象方法需要使用default 关键词修饰 (JAVA8 新特性)
-
接口中,允许出现私有方法(JAVA9 新特性)
第十八讲:任务总结
-
多态(重中之重)
基本形态, 语法格式, 多态的特点, 类型转换, instanceof,实际意义等
-
抽象类(重点)
抽象方法,抽象类,抽象类和抽象方法的关系,实际意义等
-
接口(重点)
基本概念,常量,抽象方法,弥补不能多继承的不足,接口和类之间的关系,抽象类和接口的主要区别等
任务五:特殊类
第一讲:内部类的概念和分类
<1> 基本概念
当 一个类的定义 出现在 另外一个类的 类体中 时,这个类 叫做 内部类, 而这个内部类所在的类叫做 外部类
<2>类中的内容: 成员变量,成员方法,构造方法,静态成员,构造快和静态代码块,内部类
<3>实际作用
当 一个类 存在的价值 仅仅是为 某一个类 单独服务时, 那么 就可以将 这个类定义为 所服务类中的 内部类, 这样 可以隐藏 该类的 实现细节 并且 可以方便的访问 外部类的私有成员, 而不再需要 共有的 get 和 set方法。
<4>内部类的分类
普通内部类 - 直接将一个类的定义放在另外一个类的类体中
静态内部类 - 使用static 关键字修饰的内部类,隶属于类层级
局部内部类 - 写在方法体内部 的类
匿名内部类 - 就是指没有名字的内部类
第二讲:普通内部类的定义
<1> 普通(成员)内部类的格式
访问修饰符 class 外部类类名{
访问修饰符 class 内部类类名{
内部类类体;
}
}
<2> 示例
public class NormalOuter {
private int cnt = 1;
// 定义普通内部类,隶属于外部类的成员,并且是对象层级
public class NormalInner{
private int ia = 2;
public NormalInner(){
System.out.println("普通内部类的构造方法");
}
public void show(){
System.out.println("外部类中变量cnt的数值为:" + cnt); // 1
System.out.println(" ia = " + ia); // 2
}
}
}
public class NormalOuterTest{
public static void main(String[] args){
// 1.声明NormalOuter类型的引用 指向该类型的对象
NormalOuter outer = new NormalOuter();
//2. 声明NormalOuter类中,内部类的引用,指向内部类的对象
NormalOuter.NormalInner inner = outer.new NormalInner();
// 3.调用内部类中的show方法
inner.show();
}
}
第三讲:普通内部类的使用方式
<1>
普通内部类和普通类一样可以定义成员方法变量,成员方法以及构造方法等。
<2>
普通内部类和普通类一样可以使用final或者abstract关键字修饰
<3>
普通内部类还可以使用private或protected关键字进行修饰
<4>
普通内部类需要使用外部类来创建对象
<5>
如果内部类 访问 外部类中 与本类内部同名的 成员变量 或 方法时, 需要使用 this 关键字
例子:
public class NormalOuterTest{
public static void main(String[] args){
NormalOuter outer = new NormalOuter();
NormalOuter.NormalInner inner = outer.new NormalInner();
inner.show(4);
}
}
第四讲 - 第五讲:静态内部类的定义 & 静态内部类的使用方式
<1> 语法格式
访问修饰符 class 外部类类名{
访问修饰符 static class 内部类类名{
内部类类体;
}
}
<2>代码
public class StaticOuter {
private int cnt = 1;// 隶属于对象层级
private static int snt = 2; // 隶属于类层级
public void show(){
System.out.println("外部类中的 show()方法! " );
}
private static class StaticInner{
private int ia = 3;
public StaticInner(){
System.out.println("静态内部类的构造方法哦!");
}
public void show(){
System.out.println("静态内部类的show方法! ia = " + ia);
System.out.println("外部类的 snt = " + snt);
// 不能访问外部类的cnt: System.out.println("外部类的 cnt = " + cnt);
}
public void show2(int snt){
System.out.println(" snt = " + snt); // 5
System.out.println(" 内部类中的成员snt = " + StaticInner.snt); // 4
System.out.println(" 外部类中的成员snt = " + StaticOuter.snt); // 2
// 调用外部类中的show方法
new StaticOuter().show();
}
}
}
public class StaticOuterTest { public static void main(String[] args) { // 1.声明StaticInner类型的引用指向该类型的对象 StaticOuter.StaticInner si = new StaticOuter.StaticInner(); // 2.调用show方法进行测试 si.show(); System.out.println("---------------------------------------"); si.show2(5); } }
静态内部类不能直接访问外部类的非静态成员
静态内部类可以直接创建对象
如果内部类 访问 外部类中 与本类内部同名的 成员变量 或 方法时, 需要使用 类名. 的方式访问
第六讲:局部内部类的定义
<1> 局部(方法)内部类的格式
访问修饰符 class 外部类的类名{
方法(){
class 内部类的类名{
内部类的类体;
}
}
}
<2> 局部内部类
public class AreaOuter{
private int cnt = 1;
public void show(){
// 定义一个局部变量进行测试
// 从Java8开始,直接默认理解为 final关键字 修饰的变量
// 虽然可以省略final,但是建议加上
/*final */ int ic = 4;
// 定义局部内部类,只在当前方法体的内部好使
class AreaInner{
private int ia = 2;
public AreaInner(){
System.out.println("局部内部类的构造方法!");
}
public void test(){
System.out.println(" ia = " + ia);// 2
System.out.println(" cnt = " + cnt);// 1
}
// 声明局部内部类的引用,指向局部内部类的对象
AreaInner ai = new AreaInner();
ai.test();
}
}
}
public class AreaOuterTest{
public static void main (String[] args){
// 1. 声明:外部类类型的引用,指向外部类的对象
AreaOuter ao = new AreaOuter();
// 2. 通过show方法的调用,实现局部内容类的定义和使用
ao.show();
}
}
<3> 使用方式
只能在方法内部使用
可以在方法体内部直接创建对象
不能使用访问控制符和static关键字修饰符
可以使用外部方法的局部变量,但必须是final的。 由局部内部类和局部变量的声明周期不同所致。
第八讲:回调模式 - 第九讲:匿名内部类的使用
<1>概念
回调模式是:如果一个方法的参数,是接口类型,则,在调用该方法时,需要创建,并,传递一个实现此接口类型的对象; 而,该方法,在运行时,会调用到,参数对象中,所实现的方法
<2>
public interface AnonymousInterface{
// 自定义抽象方法
public abstract void show();
}
public interface AnonymousInterfaceTest{
// 假设已有下面的方法, 则:
// AnonymousInterface ai = new AnonymousInterfaceImps() ;
// 接口类型的引用指向实现类型的对象,形成了多态
public static void test(AnonymousInterface ai){
// 编译阶段调用父类版本,运行调用实现类重写的版本
ai.show();
}
public static void main(String[] args){
// 请问如何调用 test(AnonymousInterface ai)?
AnonymousInterfaceTest.test (new AnonymousInterfaceImps() );
// 第九讲:如果AnonymousInterfaceImps()中的方法只需要使用一次? 有没有更好的方法?
// 方法一: 使用匿名内部类的语法格式来得到接口类型的引用,
// 格式为: 接口/父类类型引用变量名 = new 接口/父类类型() {方法的重写};
AnonymousInterface ait = new AnonymousInterface(){
@Override
public void show(){
System.out.println("匿名内部类");
}
AnonymousInterfaceTest.test(ait);
// 二:从Java8开始提出新特性: Lamda表达式,可以简化上述代码, 格式为:
// (参数列表) -> {方法体}
AnonymousInterface ait2 = () -> System.out.println("Lamda表达式是如此简单!");
AnonymousInterfaceTest.test(ait2);
}
}
}
public class AnonymousInterfaceImps implements AnonymousInterface{
@Override
public void show(){
System.out.println("实现了show方法");
}
}
<3> 匿名内部类的语法格式(重点)
接口/父类类型引用变量名=new 接口/父类类型() {方法的重写};
第十讲:枚举类的概念 和 自定义实现
<1> 为什么需要枚举?
季节: 春,夏,秋,冬
性别: 男,女
键盘上的所有方向键: 上,下,左,右
这类事物,只有几个明确的固定值,可以一一列举出来
<2>代码:
// 编程实现所有方向的枚举:上,下,左,右
public class Direction {
private final String description; // 用于表述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
private Direction (String description){
this.description = description;
}
public String getDescription(){
return description;
}
public static final Direction UP = new Direction("向上");
public static final Direction DOWN = new Direction("向下");
public static final Direction LEFT = new Direction("向左");
public static final Direction RIGHT = new Direction("向右");
}
public class DirectionTest {
public static void main(String[] args){
Direction d1 = Direction.UP;
System.out.println("获取到的方向是:" + d1.getDescription());
}
}
第十一讲:枚举类型的定义
<1>
使用public static final表示的常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型。
<2> 代码
public static final enum DirecitionEnum{
// 枚举类型要求所有枚举值必须放在枚举类型的最前面
UP("向上"), DOWN("向下"), LEFT("向左"), RIGHT("向右");
private final String description;
private DirectionEnum(String description){
this.description = description;
}
public String getDescription(){
return Description;
}
}
public class DirectionTest{
DirectionEnum de = DirectionEnum.DOWN;
System.out.println("获取到的方向是:"+ de.getDescription());
}
<3> 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final 关键字共同修饰, 因此采用枚举类型. 的方式调用
<4> 枚举类可以自定义构造方法,但是构造方法的修饰必须是 private, 默认也是私有的
第十二讲:自定义类和枚举类型在switch结构的使用
public class DirectionUseTest{
// 自定义静态方法,实现根据参数 指定的 字符串内容,来打印 具体的 方向信息.
public static void test1(String str){
switch (str){
case "向上":
System.out.println("抬头望明月"); break;
case "向下":
System.out.println("低头思故乡"); break;
case "向左":
System.out.println("左牵黄 ");break;
case "向右":
System.out.println("右擎苍 ");break;
deafult:
System.out.println("没有这样的方向哦");
}
}
// 接收枚举类型的变量
public static void test2(DirectionEnum de){
switch (de){
case UP:
System.out.println("抬头望明月"); break;
case DOWN:
System.out.println("低头思故乡"); break;
case LEFT:
System.out.println("左牵黄 ");break;
case RIGHT:
System.out.println("右擎苍 ");break;
deafult:
System.out.println("没有这样的方向哦");
}
}
public static void main(String[] args){
DirectionUseTest.test1(Direction.UP.getDescription());
DirectionUseTest.test2(DirectionEnum.DOWN)
}
}
第十三讲:Enum类的概念和方法
<1> 所有的枚举类都继承自 java.lang.Enum类, 常用方法如下:
<2> 代码示例
第十四讲:Enum类的常用方法
第十五讲:枚举类实现接口的方式
调用重写后的show方法
第十六讲:注解的概念
Annotation, 是一种引用数据类型。
可以把注解看做是一种 特殊的接口。 (比喻为: 超市货架上的标签)
注解,本质上是 代码中的特殊标记, 通过标记,可以在编译,运行,类加载时, 执行指定的处理。
第十七讲:注解的定义和使用
<1> 格式
访问修饰符 @interface 注解名称{
注解成员;
}
<2>
<3> 代码示例
第十八讲:元注解之 Retention
元注解 - “标签的标签”
代码示例
第十九讲:元注解之Documented
示例
示例
第二十讲: @Target 和 @Inherited
示例
在 Person.java 中:
示例
第二十一讲:@Repeatable
================================================================================
Plus:
第二十二讲: 常见的预制注解
第二十三讲:任务总结
1. 内部类
概念,普通内部类,静态内部类,局部内部类,匿名内部类,回调模式等
2. 枚举类型
概念,自定义枚举类,enum关键字,继承Enum类,实现接口等
3. 注解
概念,自定义注解,使用,元注解,预制注解等