面向对象
1.面向对象编程(基础)
1.1类与对象
-
类就是数据类型
-
对象就是一个具体的实例
//创建一个猫对象(实例化) Cat cat1 = new Cat(); cat1.name = "**"; cat1.age = 2; cat1.color = "橘色" //创建猫类 class Cat{ //属性 //名字 String name; //年龄 int age; //颜色 String color; public void speak(){ System.out.println("喵喵喵"); } }
-
对象在内存中存在形式
【注意】
— 基本数据类型放在堆中,引用类型和字符串放在方法区中,方法区还存放类信息(属性信息和方法信息)
-
属性/成员变量
- 从概念或叫法上看: 成员变量 = 属性 = field(字段) 即成员变量是用来表示属性的
- 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)
【注意事项】
- 属性的定义语法同变量 访问修饰符 属性类型 属性名 四种访问修饰符: public protected 默认 private
- 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一样
-
创建对象
-
先声明再创建
Cat cat; cat = new Cat();
-
直接创建
Cat cat = new Cat();
-
-
访问属性
- 基本语法 对象名.属性名
-
类和对象的内存分配机制(重要)
Person p1 = new Person(); p1.age = 10; p1.name = "张三"; Person p2 = p1; System.out.println(p2.age);
8.类和对象的内存分配机制
-
栈:一般存放基本数据类型(局部变量)
-
堆:存放对象
-
方法区:常量池,类加载信息
-
简单分析
Person p = new Person(); p.name = "jack"; p.age = 12;
【创建对象的流程简单分析】
- 先加载Person类信息(属性和方法信息,只会加载一次)
- 在堆中分配空间,进行默认初始化
- 把地址赋给p,p就指向对象
- 进行指定初始化
1.2成员方法
-
基本介绍
- 在某些情况下,需要定义成员方法,对类进行完善
-
成员方法快速入门
public static void main(String[] args) { Scanner scan = new Scanner(System.in); Person p = new Person(); p.speak(); p.sumNum(); System.out.println("请输入1~n的数字,计算和"); p.sumNum2(scan.nextInt()); System.out.println("请输入需要求和的两个数字"); int sum = p.getSum(scan.nextInt(), scan.nextInt()); System.out.println(sum); } class Person{ String name; int age; String sex; //成员方法 public表示方法公开 void表示没有返回值 speak表示方法名 ()可以添加形参列表 {}方法体 public void speak(){ System.out.println("我是一个好人"); } public void sumNum(){ int sum = 0; for (int i = 1;i <= 100; i++){ sum += i; } System.out.println(sum); } public void sumNum2(int num){ int sum = 0; for (int i = 1;i <= num; i++){ sum += i; } System.out.println(sum); } public int getSum(int num1,int num2){ int sum = num1 + num2; return sum; } }
-
方法的调用机制【图示】
【总结】
— 当程序执行到方法时,会开辟一个独立的空间(栈空间)
— 当方法执行完毕,或者执行到return语句时,就会返回,并且销毁方法
— 返回到调用方法的地方
— 返回后,继续执行方法后面的代码
— 当main方法(栈)执行完毕,整个程序退出
-
成员方法的定义
修饰符 返回数据类型 方法名 (形参列表){ //方法体 语句; return 返回值; }
- 形参列表:表示成员方法输入参数
- 数据类型(返回类型):表示成员方法输出,void表示没有返回值
- 方法主体:表示为了实现某一个功能
- return 语句不是必须的
- 如果方法没有返回值void,可以不写,也可以return;
-
方法递归(recursion)的调用
public class TwoDimensionalArray02 { public static void main(String[] args) { Recursion recursion = new Recursion(); recursion.test(4); } } class Recursion{ public void test(int n){ if(n > 2){ test(n - 1); } System.out.println("n="+n); } } //输出结果: n = 2 n = 3 n = 4
【图示流程】
【注意】 方法栈执行结束,会被cg回收
-
递归重要规则
-
执行一个方法时,就创建一个新的受保护的独立空间(栈)
-
方法的局部变量是独立的,不会相互影响
-
如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据
-
递归必须退出递归的条件逼近,否者就会无限递归,出现栈溢出
-
当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
【【递归案例—老鼠寻路】】
public class TwoDimensionalArray02 { public static void main(String[] args) { Maze maze = new Maze(); maze.getMap(); } } class Maze{ //生成地图 public void getMap(){ int[][] map = new int[8][7]; for (int i = 0;i < map[1].length;i++){ map[0][i] = 1; map[map.length-1][i] = 1; } for (int j = 0; j < map.length; j++){ map[j][0] = 1; map[j][map[1].length-1] = 1; } map[3][1] = 1; map[3][2] = 1; for (int k = 0; k < map.length; k++){ for(int l = 0;l < map[k].length;l++){ System.out.print(map[k][l]+"\t"); } System.out.println(); } track(map,1,1); //老鼠运动的轨迹 for (int k = 0; k < map.length; k++){ for(int l = 0;l < map[k].length;l++){ System.out.print(map[k][l]+"\t"); } System.out.println(); } } //图标位移 /** * * @param map * @param i * @param j * @return 布尔值 * * 1.如果找到就返回true,否则返回false * 2.map是一个二维数组,即表示迷宫 * 3.i,j就是老鼠的位置,初始化的位置为(1,1) * 4.使用递归找路线,先规定map数组的各个值的含义 0表示可以走(还没走) 1表示障碍物 2表示可以走(走了) 3表示走过,但是走不通是死路 * 5.map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找 * 7.老鼠找路策略 下-》右-》上-》左 */ public boolean track(int[][] map,int i,int j){ if (map[6][5] == 2){ return true; }else{ if (map[i][j] == 0){ //假定可以走通 map[i][j] = 2; //使用找路策略,来确定该位置是否真的可以走通 if(track(map,i + 1,j)){ return true; }else if(track(map,i,j + 1)){ return true; }else if(track(map,i - 1,j )){ return true; }else if (track(map,i,j - 1)){ return true; }else{ map[i][j] = 3; return false; } }else{ return false; } } } }
【【递归案例—汉罗塔】】
public class HanoiTower { public static void main(String[] args) { Tower tower = new Tower(); tower.move(5,'A','B','C'); } } class Tower{ //num 表示要移动的个数 a,b,c分别表示A塔 B塔 c塔 public void move(int num,char a,char b,char c){ if(num == 1){ System.out.println(a+"--->"+c); }else{ //先移动上面所有的盘到b盘,借组c move(num - 1,a,c,b); System.out.println(a+"--->"+c); //再把b塔的所有盘,移动到c,借助a move(num - 1,b,a,c); } } }
【【递归案例—八皇后】】
public class EightQueens { /** * 数组最大容量 * */ private int max = 8; //记录列的数组 private int[] arr = new int[max]; //解法数 private static int wayCount = 0; //回溯次数 private static int recursionCount = 0; /** * 检测通路 * @param n */ public void checkWay(int n){ if (n == max){ showWay(); return; } //给列赋值,同时判断通路 for(int i = 0;i < max;i++){ //给列赋值 arr[n] = i; //如果是通路 if(judgeWay(n)){ checkWay(n+1); } } } /** * 判断是否为通路 * * @param n * @return */ public boolean judgeWay(int n){ recursionCount++; for (int i = 0;i < n; i++){ if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])){ return false; } } return true; } public void showWay(){ wayCount++; for (int i = 0;i < arr.length;i++){ System.out.print(arr[i]+"\t"); } //换行 System.out.println(); } public static void main(String[] args) { EightQueens eightQueens = new EightQueens(); eightQueens.checkWay(0); System.out.println("wayCount = "+wayCount); System.out.println("recursionCount = "+recursionCount); } }
-
1.3成员方法传参机制
- 基本数据类型的传参机制
public class TwoDimensionalArray02 {
public static void main(String[] args) {
int a = 10;
int b = 20;
Param p = new Param();
p.swap(a,b);
System.out.println("a="+a+";b="+b);// a=10 b=20
}
}
//开辟一个独立的空间(栈空间)
class Param{
public void swap(int a,int b){
int tmp = a;
a = b;
b = tmp;
System.out.println("a和b交换后的值"+";\ta="+a+"\tb="+b);//a=20 b=10
}
}
-
引用数据类型的传参机制(传递的地址,可以通过形参影响实参)
public class TwoDimensionalArray02 { public static void main(String[] args) { Param param = new Param(); int[] arr = {10,20,30}; param.updateArr(arr); for (int num : arr){ System.out.print(num+"\t");//100 20 30 } } } /** *1.创建Parma对象,指向堆;创建数组,指向堆 *2.产生一个新栈方法空间,引用类型传递是地址,指向堆 *3.在堆中找到arr第一个元素修改为100,方法中的for循环,输出 100 20 30,方法结束 *4.回到主栈,通过for循环遍历,arr指向还是堆中的位置,输出 100 20 30 *【注意】 * 在方法中,如果将引用类型的地址置空,并不会影响主栈(main)的引用 * public void setNUm(person person){ * person = null; * } * * */ class Param{ public void updateArr(int[] arr){ arr[0] = 100; for(int num : arr){ System.out.print(num+"\t");// 100 20 30 } System.out.println(); } }
1.4overload
-
基本介绍
-
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
public class overload_01 { public static void main(String[] args) { MyCalulator myCalulator = new MyCalulator(); System.out.println(myCalulator.calculator(1,2));// 4 System.out.println(myCalulator.calculator(2.1,2));// 4.1 System.out.println(myCalulator.calculator(2,2.5));// 4.5 System.out.println(myCalulator.calculator(1,2,3));// 6 } } class MyCalulator{ public int calculator(int num,int numx){ return num+numx; } public double calculator(int numx,double num){ return num+numx; } public double calculator(double num,int numx){ return num+numx; } public int calculator(int num,int numx,int nums){ return num+numx+nums; } }
-
-
注意事项和使用细节
- 方法名相同
- 形参列表不同,形参类型或个数或顺序,至少有一个不同,参数名无要求
- 返回类型无要求
1.5可变参数
-
基本概念
- java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现
-
基本语法
-
访问修饰符 返回类型 方法名(数据类型… 形参名){ }
public class VarParamter { public static void main(String[] args) { VarParameter paramter = new VarParameter(); System.out.println(paramter.sum(1,5,8,19));// 33 } } class VarParameter{ //int... 表示接受的是可变参数,类型是int,即可以接收多个int(0 ~多) public int sum(int... nums){ int res = 0; for (int i = 0; i < nums.length; i++){ res += nums[i]; } return res; } }
-
-
可变参数的注意事项
-
可变参数的实参可以为0个或任意多个
-
可变参数的实参可以为数组
//方法可以直接传递数组 .方法名(数组) 修饰符 返回类型 方法名 (数据类型... 形参名){}
-
可变参数的本质就是数组
-
可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数的最后*
-
一个形参列表中只能出现一个可变参数
-
1.6作用域
-
基本使用
- 面向对象中,变量作用域是非常重要知识点,相对来说不是特别好理解
- 在Java编程中,主要的变量就是属性(成员变量)和局部变量
- 局部变量一般是指在成员方法中定义的变量
- java作用域的分类
- 全局变量,也就是属性,作用域(scope)为整个类体
- 局部变量,也就是出了属性之外的其他变量,作用域为定义它的代码块中
- 全局变量可以不赋值,直接使用,因为有默认值;局部变量必须赋值后,才能使用,局部变量没有默认值
-
注意事项和使用细节
-
属性和局部变量可以重名,访问时遵循就近原则
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
-
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡;局部变量,声明周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中
-
修饰符不同
- 全局变量/属性可以加修饰符
- 局部变量不可以加修饰符
-
作用域范围不同
- 全局变量:可以被本类使用,或其他类使用(通过对象调用)
- 局部变量:只能在本类中对应的方法中使用
//属性和局部变量,访问时遵循就近原则 public class VarScope { public static void main(String[] arg){ VarParameters varParameters = new VarParameters(); System.out.println(varParameters.sum(23));// 46 } } class VarParameters{ int num = 12; public int sum(int numx){ int num = 23; return num + numx; } }
-
1.7构造器
-
基本语法
- [修饰符] 方法名 (形参列表){ 方法体; }
- 说明
- 构造器的修饰符可以默认
- 构造器没有返回值
- 方法名和类名字必须一样
- 形参列表和成员方法一样的规则
- 构造器的调用由系统来完成
-
基本介绍
- 构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成了新对象的初始化,以下是它的特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动调用该类的构造器完成对象的初始化
//简单的构造器演示 public class Constructors { public static void main(String[] args) { Student student = new Student("张三",20); System.out.println(student.name+";"+student.age); } } class Student{ String name; int age; public Student(){ } public Student(String name,int age){ this.name = name; this.age = age; } public Student(String name){ this.name = name; } public Student(int age){ this.age = age; } }
- 构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成了新对象的初始化,以下是它的特点:
-
注意事项和使用细节
-
一个类可以定义多个不同的构造器,即构造器重载
-
构造器名和类名要相同
-
构造器没有返回值
-
构造器时完成对象的初始化,并不是创建对象
-
在创建对象时,系统自动的调用该类的构造器
-
如果没有定义构造器,系统会默认给类生成一个默认无参构造方法(可以通过javap指令 反编译)
- 使用语法
- javap -c -v 类名
- 使用语法
-
一旦定义了自己的构造器,默认的构造器就被覆盖,就不能再使用无参构造器,可以再次定义无参构造器
-
构造器可以被private修饰,类的构造器私有化,因此类不能继承,同时也不能被实例化
public class Constructors { public static void main(String[] args) { System.out.println(Student.getInstance().name); } } class Student{ String name; int age; private static final Student INSTANCE = new Student("李氏",25); public static Student getInstance(){ return INSTANCE; } private Student(String name,int age){ this.name = name; this.age = age; } }
-
1.8this
1.对象创建的流程分析
public class ThisKey {
public static void main(String[] args) {
/**
* 1.在方法区中加载Persons类
* 2.在堆中开辟一个对象空间(假设地址 0x11111),属性最初进行默认值初始化
* 3.再查看类是否赋值,如果有值,就替换默认值
* 4.构造器初始化,常量池存放String(非基本类型),堆中name属性指向常量池(假设地址 0x111122)
* 5.栈中创建persons指向堆中的对象(假设地址 0x11111)
*/
Persons persons = new Persons("张三",23);
System.out.println(persons.name+";"+persons.age);
}
}
class Persons{
String name;
int age;
Persons(){}
Persons(String name,int age){}
}
2.this概念
- java虚拟机会给每个对象分配一个this,代表当前对象
- 哪个对象调用,this就代表哪个对象
3.注意事项和使用细节
- this关键字可以用来访问本类的属性、方法、构造器等
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法: this.方法名(形参列表)
- 访问构造器语法: this(形参列表) 【注意】只能在构造器中使用
- this不能在类定义的外部使用,只能在类定义的方法中使用
2.面向对象编程(中级)
2.1包
1.包的三大作用
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
2.包基本语法
package com.huawei.**;
//说明
1.package 关键字,表示打包
2.com.huawei.** 表示包名
3.包的本质分析(原理)
- 包的本质: 实际上就是创建不同的文件夹来保存类文件
4.包的命名
- 命名规则
- 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
- 命名规范
- 一般是小写字母+小圆点
- com.公司名.项目名.业务模块名
5.引入包
- 【建议】 需要使用哪个类,就引入哪个类
//表示只会引入java.util包下的Scanner
import java.util.Scanner;
//表示引入java.util包下的所有类
import java.util.*;
2.2访问修饰符
1.基本介绍
- java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)
- 公开级别: public修饰,对外公开
- 受保护级别: protected修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符符号,向同一个包的类公开
- 私有级别:private修饰,只有类本身可以访问,不对外公开
【使用的注意事项】
- 修饰符可以用来修饰类中的属性、成员方法以及类
- 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
- 成员方法的访问规则和属性完全一样
2.3封装(encapsulation)
1.封装介绍
- 封装就是把抽象出来的数据[属性]和对数据操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作
2.封装的理解和好处
- 隐藏实现细节
- 可以对数据进行验证,保证安全合理
3.封装实现步骤
-
将属性进行私有化(private)
-
提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){ //加入数据验证的业务逻辑 this.属性 = 参数名; }
-
提供一个公共的get方法,用于获取属性的值
public XX getXxxx(){ return XX; }
2.4继承
1.基本介绍
-
继承可以解决代码复用,让我们的编程更加靠近人类的思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可
-
【示意图】
2.继承的基本语法
class 子类 extends 父类{}
//1.子类就会自动拥有父类定义的属性和方法
//2.父类又叫超类或基类
//3.子类又叫派生类
3.继承的优势
- 代码的复用性提高了
- 代码的扩张性和维护性提高了
4.继承使用细节
-
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问
class Person{ public String username; protected int age; String jod; private double salary; //基类可以提供公共方法来实现 public double getSal(){ return this.salary; } private void salaryDetail(){ System.out.println("多个项目构成") } public void getSalaryDetail(){ salaryDetail(); } } class employees extends Person{ //在子类中访问salary getSal(); //在子类中访问salaryDetail()fangfa getSalaryDetail(); }
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
class Person{ public String username; protected int age; String jod; private double salary; public Person(String username,int age){ this.username = username; this.age = age; System.out.println("父类Person(String username,int age)构造器被调用"); } } class Stu extends Person{ public Stu(){ super("Tom",20); System.out.println("子类Stu()构造器被调用") } public Stu(String username){ //如果在此处不指定,则编译不会通过 super("Tom",20); System.out.println("子类Stu(String username)构造器被调用") } } class Test{ public static void main(String[] args){ Stu stu = new Stu("张三"); } } //父类Person(String username,int age)构造器被调用 //子类Stu(String username)构造器被调用
-
如果希望指定去调用父类的某个构造器,则显式调用一下
super([参数列表]);
-
super在使用时,需要放在构造器第一行(super只能在构造器中使用)
-
super()和this()都只能放在构造器第一行,因此这两个方法不能共同存在一个构造器中(this()调本类的构造器)
-
java所有类都是Object类的子类,Object是所有类的基类
-
父类构造器的调用不限于直接父类,将一直往上追溯到Object类(顶级类)
-
子类最多只能继承一个父类(指直接继承),即java中是单继承机制
-
不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
5.继承的本质【重点】
-
当子类对象创建后,建立查找关系
-
查找关系
- 首先看看子类是否有该属性,如果子类有这个属性,并且可以访问,则返回信息
- 如果子类没有这个属性,就查看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)
- 如果父类没有就按照(3)的规则,继续找上级父类,知道Object…
2.5多态
1.多态的基本介绍
-
方法或者对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础上的
-
对象的多态
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型时可以变化的
- 编译类型是看定义时等号的左边,运行类型看等号的右边
public class Animal { public void cry(){ System.out.println("动物在叫"); } } -------------------------------------------------------------------------------- public class Dog extends Animal { public void cry(){ System.out.println("汪汪汪"); } } -------------------------------------------------------------------------------- public class Cat extends Animal { public void cry(){ System.out.println("喵喵喵"); } } ------------------------------------------------------------------------------- public class PolyObject { public static void main(String[] args) { //ann 编译类型是Animal,运行类型Animal Animal ann = new Animal(); ann.cry();//动物在叫 // ann 编译类型是Animal,运行类型是Cat ann = new Cat(); ann.cry();//喵喵喵 // ann 编译类型是Animal,运行类型是Cat ann = new Dog(); ann.cry();//汪汪汪 } }
2.多态注意事项和细节讨论
- 多态的前提是:两个对象(类)存在继承关系
- 多态的向上转型
- 本质: 父类的引用指向了子类的对象
- 语法: 父类类型 引用名 = new 子类类型();
- 特点: 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需要遵守访问权限)
- 不能调用子类中特有成员
- 最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法,然后调用。
- 编译阶段,能调用哪些成员,由编译类型决定的
- 多态的向下转型
- 语法: 子类类型 引用名 = (子类类型) 父类类型;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有成员
//向上转型
Animal animal = new Cat();
//向下转型
Cat cat = (Cat) animal;
//会报ClassCastException,类异常 animal指向堆中的cat对象,不能转为dog对象
Dog dog = (Dog) animal;
-
属性没有重写之说!属性的值看编译类型【重点】
public class PolyDetail2 { public static void main(String[] args) { Base base = new Sub(); System.out.println(base.count);//10 Sub sub = (Sub) base; System.out.println(sub.count);//20 } } class Base{ int count = 10; } class Sub extends Base{ int count = 20; }
-
instanceOf比较操作符,用于判断对象的运行类型是否为xx类型或者XX类型的子类型
public class PolyDetail3 { public static void main(String[] args) { BB bb = new BB(); System.out.println(bb instanceof BB);// true System.out.println(bb instanceof AA);//true AA aa = new BB(); System.out.println(aa instanceof AA);//true System.out.println(aa instanceof BB);//true } } class AA{ } class BB extends AA{ }
3.java的动态绑定机制[重点]
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class Dynamic {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());//40
System.out.println(a.getl());//30
//注释 B类中的sum()和sum1()方法
System.out.println(a.sum1());//30
System.out.println(a.getl());//20
}
}
class A{
public int i = 10;
//动态绑定
public int sum(){
return getl() + 10;
}
public int sum1(){
return i + 10;
}
public int getl(){
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i + 20;
}
public int sum1(){
return i;
}
public int getl(){
return i + 10;
}
}
4.多态的应用
public class Employee {
private String name;
private double salary;
public Employee() {
}
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getAnnual(double salary){
return 12 * salary;
}
}
---------------------------------------------------------------------------------------
public class Worker extends Employee{
public Worker() {
}
public Worker(String name, double salary) {
super(name, salary);
}
public void work(String name){
System.out.println(name+"在认真工作");
}
public double getAnnual(double salary){
return super.getAnnual(salary);
}
}
--------------------------------------------------------------------------------------
public class Manager extends Employee{
private double bonus;
public Manager() {
}
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage(String name){
System.out.println(name+"管理公司正常运行");
}
public double getAnnual(double bonus,double salary){
return super.getAnnual(salary) + bonus;
}
}
-----------------------------------------------------------------------
public class PloyParamter {
public static void main(String[] args) {
Worker worker = new Worker("李四",8000);
Manager manager = new Manager("张三",15000,50000);
PloyParamter ployParamter = new PloyParamter();
ployParamter.showEmployeeAnnual(worker);//96000
ployParamter.showEmployeeAnnual(manager);//180000
ployParamter.testWork(worker);//李四在认真工作
ployParamter.testWork(manager);//张三管理公司正常运行
}
public void showEmployeeAnnual(Employee emp){
System.out.println(emp.getAnnual(emp.getSalary()));
}
public void testWork(Employee employee){
if (employee instanceof Worker){
((Worker) employee).work(employee.getName());
} else if(employee instanceof Manager){
((Manager) employee).manage(employee.getName());
}
}
}
2.6Super
1.基本介绍
- super代表父类的引用,用于访问父类的属性、方法、构造器
2.基本语法
- 访问父类的属性,但不能访问父类private属性 super.属性名; ------就近原则
- 访问父类的方法,但不能访问父类的private方法 super.方法名([参数列表])
- 访问父类的构造器 **super(参数列表) **只能放在构造器的第一句,只能出现一句
3.super和this的比较
区别点 | this | super |
---|---|---|
访问属性 | 访问本类的属性,如果本来没有此属性则从父类中继续查找 | 访问父类中的属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 直接访问父类的方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类的构造器,必须放在子类构造器的首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
2.7overwrite
1.基本介绍
- 方法覆盖(重写)就是子类有一个方法,和父类的某个方法名称、返回类型、参数列表一样,那么我们就说子类的这个方法覆盖了父类的那个方法
2.注意事项和使用细节
- 子类的方法的参数、方法名称,要和父类方法的参数,方法名称完全一样
- 子类方法的返回类型和父类返回类型一样,或者是父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限
3.发生覆盖的条件
- ”三同一不低“子类和父类方法名称、参数列表、返回类型必须相同(注:JDK>= 1.7,返回类型也可以是父类返回值的派生类),子类方法的访问修饰符权限不能比父类低
- 子类方法不能抛出比父类方法更多异常;即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出
- 被覆盖的方法不能是final类型
- 被覆盖的方法不能为private
- 被覆盖的方法不能为static
4.重载和重写的比较
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重写(override) | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者为其子类 | 子类方法不能缩小父类方法的访问范围 |
重载(overload) | 本类 | 必须一样 | 类型、个数或顺序至少有一个不同 | 无要求 | 无要求 |
2.8Object类详解
1.equals()方法
- == 和 equals的对比
- ==:既可以判断基本类型,又可以判断引用类型
- ==:如果判断基本类型,判断的是值是否相等
- ==:如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
- equals: 是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
2.hashCode()
- 提供具有哈希结构的容器效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的! 不能完全将哈希值等价于地址
3.toString()
-
默认返回: 全类名 + @ +哈希值的十六进制
//源码 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
4.finalize()
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收: 当某个对象没有引用时,则jvm就认为这个对象时一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象之前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定,也可以通过**System.gc()**主动触发垃圾回收机制