今日语录:起风了,唯有努力生存。 —-宫崎骏《起风了》
一、类与对象
-
类和对象的区别和联系
1、类是抽象的,概念的,代表一类事物,比如🐕类、🐱类,🐘类…,即它是数据类型
2、对象是具体的,实际哦的,代表一个具体事物,即 实例
3、类是对象的模板,对象是类的一个个体,对应的一个实例。
对象在内存中示例图解:

-
属性 & 成员变量
1、 从概念或叫法上看:成员变量 = 属性 = field(字段)(即 成员变量是用来表示属性的,授课中,统一叫 属性)
2、属性是类的一个组成部分,一般是基本数据,也可以是引用类型(对象、数组)。
比如我们前面定义锚类 的 int age 就是属性
-
注意事项和细节说明
1、属性的定义语法通变量,示例:访问修饰符 属性类型 属性名;
2、属性的定义类型可以为任意类型,包含基本类型和引用类型
3、属性如果不复制,有默认值,规则和数组一致。
-
简介 -
如何创建对象
1、先声明再创建
如:Cat cat ;
cat = new Cat();
2、直接创建
如:Cat cat = new Cat(); -
类和对象的内存分配机制
1、栈:一般存放基本数据类型(局部变量)
2、堆:存放对象(Cat cat ,数组等)
3、方法区:常量池(常量,比如字符串),类加载信息
4、示意图:Person (name,age)
二、成员方法
-
方法调用机制图解
-
成员方法的好处
1、提高代码的复用性
2、可以将实现的细节封装起来,然后供其他用户来调用即可
-
成员方法的定义
public 返回数据类型 方法名(形参列表..){
//方法体
语句;
return 返回值;
}1、形参列表:表示成员方法输入cal(int n),getSum(int num1,int num2);
2、返回数据类型:表示成员输出,void表示没有返回值
3、方法主体:表示为了实现某一功能代码块
4、return语句不是必须(方法是void类型)
-
使用细节和注意事项
-
访问修饰符(作用是控制 方法使用的范围)
如果不写默认访问,[有四种:public protected,default,private]
-
返回数据类型
1、一个方法最多有一个返回值
2、返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
3、如果方法要求有-返回数据类型,则方法体重最后的执行语句必须为 return 返回值;而且要求返回值类型必须和return的值类型一致或兼容。
4、如果方法是void,则方法体中可以没有return语句,或者只写return;
方法名
遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如:得到两个数的和getSum,开发中按照规范
-
形参列表
1、一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如 getSum(int n1,int n2);
2、参数类型可以为任意类型,包含基本类型或引用类型,
比如 printArr(int[][] map);
3、调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
4、方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参,实参和形参的类型要一直或兼容、个数、顺序必须一致
-
方法体
里面写完成功能的具体语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但是里面不能再定义方法!即:方法不能嵌套定义
-
方法细节调用说明
1、同一个类中的方法调用:直接调用即可。如print(参数);
2、跨类中的方法A调用B类的方法:需要通过对象名调用。比如 对象名.方法名(参数);
3、跨类的方法调用和方法的访问修饰符相关。
三、方法递归调用
class FactorialDemo{
public static void(String[] args){
T t = new T();
int result = t.factorial(5);
System.out.println(result);
}
}
class T{
public int factorial(int n){
if(n == 1){
return 1;
}else{
return factorial(n-1) * n;
}
}
}
运行结果:120
运行图解:
-
递归重要规则
1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2、方法的局部变量是独立的,不会相互影响,比如n变量
3、如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据。
4、递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverFlowError,堆栈溢出错误
5、当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
-
斐波那契数
要求:请使用递归的而方式求出斐波那契数1,1,2,3,5,8,13,21...给你一个整数n,求出它的值是多少?
分析:
/**
* 1、当 n = 1,斐波那契数 是 1
* 2、当 n = 2,斐波那契数 是 1
* 3、当 n >= 3,斐波那契数是前两个数的和
* 4、这里就是一个递归的思路
*/
public class Fibonaci{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("请输入斐波那契数的位置为第几个:");
int input = scan.nextInt();
T t = new T();
int res = t.fibonaci(input);
System.out.println("第"+input+"个斐波那契数的数值为:"+res);
}
}
class T{
public int fibonaci(int n){
if(n >= 1){
if(n == 1 || n == 2){
return 1;
}else{
return fibonaci(n-1) + fibonaci(n-2);
}
}else{
System.out.println("要求输入的为 n >= 1 的整数");
}
return n;
}
}/**
* 1、先创建迷宫,用二维数组表示,int[][] map = new int[8][7];
* 2、先规定map数组的元素值:0表示可以走 1表示障碍物
* 3、day = 8时 有 (day9 + 1)* 2 = 10
* 4、......
* 5、规律:前一天的桃子 = (后一天的桃子 + 1) * 2
*/
/**
* 1、先创建迷宫,用二维数组表示,int[][] map = new int[8][7];
* 2、先规定map数组的元素值:0表示可以走 1表示障碍物
* 3、day = 8时 有 (day9 + 1)* 2 = 10
* 4、......
* 5、规律:前一天的桃子 = (后一天的桃子 + 1) * 2
*/
public class MiGong{
public static void main(String[] args){
int[][] map = new int[8][7];
//将最上面和最后一行,全部设为1
for(int i = 0 ; i< 7;i++){
map[0][i] = 1;
map[7][i] = 1;
}
//将最左面和最右面一列,全部设为1
for(int i = 0;i < 8; i++){
map[i][0] = 1;
map[i][6] = 1;
}
map[3][1] = 1;
map[3][2] = 1;
//输出当前地图
System.out.println("当前地图情况==============");
for(int i=0;i<map.length;i++) {
for(int j=0;j < map[i].length;j++){
System.out.print(map[i][j]+" ");
}
System.out.println();
}
// 使用findWay给老鼠找路
System.out.println("老鼠找路结果==============");
TT t = new TT();
boolean b = t.findWay(map,1,5);
for(int i=0;i<map.length;i++) {
for(int j=0;j < map[i].length;j++){
System.out.print(map[i][j]+" ");
}
System.out.println();
}
}
}
class TT {
// 使用递归回溯的思想来解决老鼠出迷宫
// 分析:
// 1、findWay方法就是专门用来找迷宫的路径
// 2、如果找到,就返回 true,否则返回 false
// 3、map 就是二维数组,即表示迷宫
// 4、i,j就是老鼠的位置,初始化的位置为(1,1)
// 5、因为我们是递归的找路,所以先规定map 数组的各个值的含义
// 0 表示可以走, 1表示障碍物 2表示可以走 3表示走过,但是走不通是死路
// 6、当map[6][5]=2就说明找到出口了,就可以结束。
// 7、先确定老鼠找路的策略:下->右->上->左
public boolean findWay(int[][] map,int i,int j){
if(map[6][5] == 2){
return true;
}else{
//当前位置0,说明可以走
if(map[i][j] == 0){
//假定可以走通
map[i][j] = 2;
//使用找路策略,,来确定该位置是否真的可以走通
// 下->右->上->左
// 向下
if(findWay(map,i+1,j)){
return true;
// 向右
} else if(findWay(map,i,j+1)){
return true;
// 向上
} else if(findWay(map,i-1,j)){
return true;
// 向左
} else if(findWay(map,i,j-1)){
return true;
} else{
map[i][j] = 3;
return false;
}
}else{
return false;
}
}
}
}运行结果:
-
猴子吃桃问题
要求:猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃一个。以后每天猴子都吃其中的一般,然后再多吃一个。当到第十天的时候,想再吃时(即还没吃),发现只有一个桃子了。问题:最初共有多少个桃子?
分析:
/**
* 1、day = 10 时 有 1个桃子
* 2、day = 9 时 有 (day10 + 1)* 2 = 4
* 3、day = 8时 有 (day9 + 1)* 2 = 10
* 4、......
* 5、规律:前一天的桃子 = (后一天的桃子 + 1) * 2
*/
class T{
public int peach(int day){
if(day == 10){
return 1;
}else if( day >= 1 || day <= 9){
return (peach(day + 1) + 1) * 2;
}else{
System.out.println("day在1-10之间的整数才可以!");
return -1;
}
}
}
public class RecursionExercise01{
public static void main(String[] args){
T t = new T();
Scanner scan = new Scanner(System.in);
System.out.println("请输入天数:");
int day = scan.nextInt(day);
int peachNum = t.peach(day);
if(peachNum != -1){
System.out.println("第"+day+"天有"+peachNum+"个桃子");
}
}
} -
老鼠出迷宫
-
汉诺塔传说
import java.util.Scanner;
/**
* 汉诺塔传说:
* 汉诺塔:又称河内塔,问题是源于印度的一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石主子,在一根 柱子上从下往上按照大小的顺序摞着64片圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
* 假若每秒钟移动一次,共需要多长时间呢?移完这些金片需要5845.54亿年以上,太阳系的预期寿命据说也就是数百亿年。真过了5845.54亿年,地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭
* @author cxrg0
*
*/
public class HannuoTower{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("请输入塔上盘子数量:");
int num = scan.nextInt();
Tower tower = new Tower();
tower.move(num, '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{
//如果有多个盘,可以看成两个,最下面的和上面的所有盘(num-1)
//(1)先移动上面所有的盘到b,借助c
move(num - 1,a,c,b);
//(2)把下面最下面的这个盘,移动到c
System.out.println(a + "-->" + c);
//(3)再把b塔的所有盘,移动到c,借助a
move(num - 1,b,a,c);
}
}
} -
八皇后问题
问题:八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。该问题是国际西洋棋棋手马克斯.贝瑟尔于1848年提出:在8*8格的国际象棋上摆放8个皇后,使其不能相互攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
分析:
1、第一个皇后先放在第一行第一列
2、第二个皇后放在第二行第一列,然后判断是否OK,如果不OK,继续放在第二列、第三列...依此把所有列放完,找到一个合适的位置
3、继续第三个皇后,还是第一列、第二列....直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正解
4、当得到一个正解后,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放在第一列的所有正解,全部得到
5、然后回头继续将第一个皇后放在第二列,后面继续循环执行1,2,3,4的步骤
说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题。
arr[8] = {0,4,7,5,2,6,1,3} //对应arr下标,即第几行,即第几个皇后,arr[i] = val;val表示第i+1个皇后,放在第i+1行的第val+1列
public class Queue8 {
// 皇后的个数
int max = 8;
// 摆放方案的个数
static int count = 0;
// 保存每个皇后的位置,因为每行只放一个皇后,所以只用了一个一维数组保存列号即可
int[] arr = new int[max];
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.layout(0);
System.out.println("八皇后问题共有"+count+"个解法");
}
// 放置皇后
private void layout(int n) {
if(n == max) {
// 这时说明已经摆放完了
System.out.println(Arrays.toString(arr));
count++;
return;
}
// 对每个皇后,都依次去尝试摆放在每一列
for(int i = 0; i < max; i++) {
arr[n] = i;
// 判断这个皇后摆放位置是否冲突
if(judge(n)) {
// 如果当前位置不冲突,继续递归,摆放下一个皇后
layout(n+1);
}
// 如果发生冲突,进入下一轮循环,尝试摆放在下一列
}
}
// 判断第n个皇后是否和前面的冲突
private boolean judge(int n) {
for(int i = 0; i < n; i++) {
// 判断是否在同一列,或一条对角线上
if(arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[i] - arr[n])) {
return false;
}
}
return true;
}
}
四、方法重载(Overload)
-
概念
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致。以提高代码的复用性
-
重载的好处
-
定义端(方法的提供者):使用相同的方法名(一个方法)来表示功能相同的(多个)方法。 -
调用端(方法的使用者):在调用的时候,可以使用相同名字(一个名字)的方法实现不同的功能。 -
重载也是多态性的体现:一个内容,可以实现多个功能 -
注意事项和使用细节
1、方法名:必须相同
2、参数列表:必须不同(参数类型、个数或顺序,至少有一样不同,参数名无要求)
3、返回类型:无要求
-
示例代码
int m(int a){
System.out.println("1");
}
String m(int a,int b){
System.out.println("2");
}
double m(int a,int b,String c){
System.out.println("2");
}
五、可变参数
-
概念
java允许将同一个类中多个同名同步功能但参数个数不同的方法,封装成一个方法
-
基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名){
} -
示例代码
public class Demo{
public static void(String[] args){
HspMethod hspMethod = new HspMethod();
hspMethod.sum(1,2,3);
}
}
class HspMethod{
public int sum(int... nums){
System.out.println(""+nums.length);
}
} -
注意事项和使用细节
1、可变参数的实参可以为0个或者任意多个
2、可变参数的实参可以是数组
3、可变参数的本质就是数组
4、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5、一个形参列表中只能出现一个可变参数
public class ParameArr{
public static void(String[] args){
int[] arr = {1,2,3,4}
HspMethod hspMethod = new HspMethod();
hspMethod.sum(arr);
}
}
class HspMethod{
// 可变参数的实参可以是数组
public int sum(int... nums){
System.out.println(""+nums.length);
}
// 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
public int sum2(String str,int... nums){
System.out.println(""+nums.length);
}
// 一个形参列表中只能出现一个可变参数(调用编译报错)
public int sum2(int... nums,double... nums){
System.out.println(""+nums.length);
}
}
六、作用域
-
基本使用
1、在Java编程中,主要变量就是属性哦(成员变量)和局部变量
2、我们说的局部变量一般是指在成员方法中定义的变量。
3、Java中作用域的分类
全局变量:也就是属性,作用域为整个类
局部变量L也就是除了属性之外的其他变量,作用域为定义它的代码块中
4、全局变量可以不复制,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
-
注意事项和细节使用
1、属性和局部变量可以重名,访问时遵循就近原则
2、在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
3、属性声明周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。即在一次方法调用过程中。
4、作用域不同
全局变量:而已被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用
5、修饰符不同
全局变量/属性可以加修饰符
局部变量不可以加修饰符
七、构造方法(构造器)
-
基本语法
[修饰符] 方法名(形参列表){
方法体;
}1、构造器的修饰符可以默认
2、构造器没有返回值
3、方法名 和类 名字必须一样
4、参数列表和成员方法一样的规则
5、构造器的调用系统完成
-
简介
构造方法又叫构造器(constructor),是类的一种特殊方法,它的主要作用是完成对新对象的初始化。他有几个特点:
1、方法名和类名相同
2、没有返回值
3、在创建对象时,系统会自动调用该类的构造器完成对象的初始化
-
示例代码
public class Constaructor01{
public static void main(String[] args){
Person p = new Person("Smith",80);
System.out.println(p.name+"今年"+p.age+"岁");
}
}
class Person{
String name;
int age;
// 构造器
public Person(String pName,int pAge){
System.out.println("构造器被调用~~~~ 完成对象的属性初始化");
name = pName;
age = pAge;
}
} -
注意事项和使用细节
1、一个类可以定义多个不同的构造器,即构造器虫子啊
比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
2、构造器名和类名要相同
3、构造器没有返回值
4、构造器时完成对象的初始化,并不是创建对象
5、在创建对象时,系统自动的调用该类的构造方法
6、如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如Person(){},使用 javap 指令反编译看看
7、一旦定义了自己的构造器,默认的构造器就被覆盖了,就不能在使用默认的无参构造器,除非显式的定义一下,如:Person(){}
public class Constaructor02{
public static void main(String[] args){
Person p = new Person("Smith",80);
System.out.println(p.name+"今年"+p.age+"岁");
Person p2 = new Person("Lucy");
System.out.println(p2.name);
}
}
class Person{
String name;
int age;
// 构造器01
public Person(String pName,int pAge){
System.out.println("构造器被调用~~~~ 完成对象的属性初始化");
name = pName;
age = pAge;
}
// 构造器02
public Person(String pName){
System.out.println("构造器被调用~~~~ 完成对象的属性初始化");
name = pName;
}
}
八、对象创建的流程分析
-
例题
class Person{
int age = 90;
String name;
Person(String n,int a){
name = n;
age = a;
}
}
Person p = new Person("小钱",20);流程分析:
1、加载Person类信息(Person.class),只会加载一次
2、在堆中分配空间(地址)
3、完成对象初始化
3.1 默认初始化 age=0 name=null
3.2 显式初始化 age = 90 name = null
3.3 构造器的初始化 age = 20,name = “小钱”
4、再把对象在堆中的地址,返回给栈中的p(p式对象名,也可以理解为式对象的引用)
九、this关键字
-
示例代码
public class This01{
public static void main(String[] args){
PersonSec p = new PersonSec("Smith",80);
System.out.println(p.name+"今年"+p.age+"岁");
}
}
class PersonSec{
String name;
int age;
// 构造器
public PersonSec(String pName,int pAge){
System.out.println("构造器被调用~~~~ 完成对象的属性初始化");
this.name = pName;
this.age = pAge;
}
} -
this简介
java虚拟机会给每个对象分配this,指代当前对象。
-
小结
简单来说,那个对象调用,this就代表哪个对象
-
this的注意事项和使用细节
1、this关键字何以用来访问本类的属性、方法、构造器
2、this用于区分当前类的属性和局部变量
3、访问成员方法的语法:this.方法名(参数列表)
4、访问构造器语法:this(参数列表);注意:只能在构造器中使用
5、this不能在类定义的外部使用,只能在类定义的方法中使用
-
例题
题目:定义Person类,里面有name、age属性,并提供compareTo比较方法,用于判断是否和另一个人相等,提供测试类TestPerson用于测试,名字和年龄完全一样,就返回true,否则返回false
public class TestPerson{
public static void main(String[] args){
PersonD p1 = new PersonD("mary",20);
PersonD p2 = new PersonD("Jack",30);
boolean b = p1.compareTo(p2);
System.out.println("p1和pa比较结果:"+b);
}
}
class PersonD{
String name;
int age;
public PersonD(String name,int age){
this.name = name;
this.age = age;
}
public boolean compareTo(PersonD p){
return this.name.equals(p.name) && this.age == p.age;
}
} -
电脑猜拳
题目:有个人Tom设计它的成员变量。电脑每次都会随机生成0,1,2
0表示 石头
1表示 剪刀
2表示 布
并要显示Tom的输赢次数(清单)
public class MoraGame{
public static void main(String[] args){
// 创建一个玩家对象
Tom t = new Tom();
// 用来记录最后输赢的次数
int isWinCount = 0;
// 创建一个二维数组,用来接收局数,Tom除权情况以及电脑出拳情况
int[][] arr1 = new int[3][3];
int j = 0;
// 创建一个一维数组,用来接收输赢情况
String[] arr2 = new String[3];
Scanner scanner = new Scanner(System.in);
for(int i = 0;i < 3; i++){
// 获取玩家出的拳
System.ut.println("请输入你要出的拳(0-拳头,1-剪刀,2-布):");
int num = scanner.nextInt();
}
}
}
class Tom{
// 玩家出拳
}
十、包
-
包的三大作用
1、区分相同名字的类
2、当类很多时,可以很好的管理类
3、控制访问范围
-
包基本语法
package co.hspedu;
说明:
1、package 关键字,表示打包
2、com.chenx:表示包名
-
包的本质分析(原理)
就是创建不同的文件夹来保存类文件,画出示意图
-
包的命名
规则:
1、只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能时关键字或保留字
规范:
1、一般是小写字母+小圆点,一般是
com.公司名.项目名.业务模块名
比如:com.boyo.qijian.model
-
常用的包
一个包下,包含很多的类,Java中常用的包有:
java.lang.* // lang包时基本包,默认引入,不需要再引入
java util.* // util包,系统提供的工具包,工具类,使用Scanner
java.net.* // 网络包,网络开发
java.awt.* // 是做java的界面开发,GUI
十一、访问修饰符
-
简介
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
1、公开级别:用 public 修斯和,对外公开
2、受保护级别:用 protected 修饰,对子类和同一个包中的类公开
3、默认级别:没有修饰符号,向同一个包的类公开
4、私有级别:用private 修饰,只有类本身可以访问,不对外公开
-
4种访问修饰符的访问范围
-
使用细节和注意事项
1、修饰符可以用来修饰类种的属性,成员方法以及类
2、只有默认的和public才能修饰类,并且遵循上述的访问权限的特点
3、因为还没学习继承,因此关于在子类中的访问权限,讲完子类后,再讲解
4、成员方法的访问规则和属性完全一样
十二、面向对象(OOP)三大特征
-
简介
面向对象三大特征为:封装、继承和多态
1、封装
-
简介
封装(encapsulation)九十八抽象出来的数据(属性)和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作
-
好处
1、隐藏实现细节:方法(连接数据库) <– 调用(传入参数)
2、可以对数据进行验证,保证安全合理
Person{name,age}
Person p = new Person();
p.name = "jack";
p.age = 12000; -
封装的实现步骤
1、将属性进行私有化【不能直接修改属性】
2、提供一个公共的set方法,用于对属性判断并赋值
public void setXxxx(){
// 加入数据验证的业务逻辑
属性 = 参数名;
}3、提供一个公共的get方法,用于获取属性的值
public XX getXxxx(){ // 权限判断
return xx;
}
2、继承
-
简介
继承可以解决代码复用,王我们的变成更加靠近人类思维方式。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可
-
继承的示意图
-
继承的基本语法
class 子类 extends 父类{
}1、子类就会自动拥有父类定义的属性和方法
2、父类又叫 超类,基类
3、子类又叫派生类
-
继承的好处
1、代码的复用性提高了
2、代码的扩展性和维护性提高了
-
继承的使用细节
1、子类继承了所有属性和方法,非私有的属性和方法可以直接访问,但是私有属性和方法不能在子类直接访问,要通过公共的方法去访问
2、子类必须调用父类的构造器,完成父类的初始化
3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不通过
4、如果希望指定去调用弗雷德某个构造器,则显示的调用一下
5、super在使用时,需要放在构造器的第一行
6、super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
7、java所有类都是Object类的子类,Object是所有类的基类
8、父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
9、子类最多只能继承一个父类(指直接继承),即java中是单继承机制
10、不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
-
继承的内存布局
-
继承例题
class A{
A(){System.out.println("a");}
A(String name){System.out.println("a name");}
}
class B extends A{
B(){System.out.println("b");}
B(String name){System.out.println("b name");} // 分析有默认的super()
}
main中:B b = new B();
输出结果是什么?
运行结果:
a
b name
b
分析:
B(String name){System.out.println("b name");}
相当于
B(String name){super; System.out.println("b name");}
super关键字
-
简介
super代表父类的引用,用于访问父类的属性,方法、构造器
-
基本语法
super();
1、访问父类的属性,但不能访问弗雷德private属性
super.属性名;
2、访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
3、访问父类的构造器
super(参数列表);只能放在构造器的第一句,只能出现一句
-
super带来的便利/细节
1、调用父类的构造器的好处(分工明确,父类属性有父类初始化,子类的属性由子类初始化)
2、当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果。
class A{
public void say(){
System.out.println("A say ...");
}
}
class B extends A{
public void test(){
// say();
// this.say();
super.say();
}
}3、super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类中的成员;入宫多个基类中都有同名的成员,使用super访问遵循就近原则。A > B > C
-
super 和 this 的比较
方法重写/覆盖(Override)
-
简介
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
-
注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面条件
1、子类的方法的参数、方法名称,要和父类方法的参数,方法名完全一样
2、子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
比如 父类 返回类型是 Object,子类方法返回类型是String
3、子类方法不能缩小父类方法的访问权限
3、多态
-
简介
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
-
多态的具体体现
对象的多态
1、一个对象的编译类型和运行类型可以不一致
2、编译类型在定义对象时,就确定了,不能改变
3、运行类型时可以变化的
4、编译类型看定义时 = 号的左边,运行类型看 = 号的右边
public class PolyObject{
public static void main(String[] args){
// 体验对象多态特点
// animal编译类型就是Animal,运行类型 Dog
Animal animal = new Animal();
// 因为运行时,执行到改行时,animal运行类型是Dog,所以cry就是Dog的cry
animal.cry();// 小狗汪汪叫
// animal 比哪一类型 Animal,运行类型就是 Cat
animal = new Cat();
animal.cry();//小猫喵喵叫
}
} -
多态注意事项和使用细节
1、多态的前提是:两个对象(类)存在继承关系
2、多态的向上转型
1、本质:父类的引用指向了子类的对象
2、语法:
父类类型 引用名 = new 子类类型();
如:Animal animal = new Cat();3、特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(须遵守访问权限)
不能调用子类中特有成员;
最终运行效果看子类的具体实现
3、 多态的向下转型
1、语法:
子类类型 引用名 = (子类类型)父类引用;
比如:
Cat cat = (Cat)animal;2、只能强转父类的引用,不能强转父类的对象
3、要求父类的引用必须指向的是当前目标类型的对象
4、可以调用子类类型中所有的成员
4、属性没有重写之说
5、instanceOf比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型
BB bb = new BB();
Object obj = null;
// instanceOf 用于判断某个变量是否为某个类的类型或者其子类型
System.out.println(bb instanceOf AA);
System.out.println(obj instanceOf AA);
java的动态绑定机制(非常重要)
1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2、当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
// 父类
class A{
public int i = 10;
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
// 子类
class B extends A{
public int i = 20;
public int sum(){
return i + 20;
}
public int getI(){
return i;
}
public int sum1(){
return i + 10;
}
}
public class DynamicBind{
public static void main(String[] args){
// main方法
A a = new B();//向上转型
System.out.println(a.sum()); // 40
System.out.println(a.sum1());// 30
}
}
-
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
例如:
// Person为父类
Person[] person = new Person[5];
// Student 和 Teacher是子类
person[0] = new Student("Jack",1);
person[1] = new Teacher("Lucy",2);
// 怎么调用不同对象特有的方法
for(int i = 0; i < person.length; i++){
if(person[i] instanceof Student){
Student student = (Student)person[i];
// 调用student的study方法
student.study();
}
if(person[i] instanceof Teacher){
Teacher teacher = (Teacher)person[i];
// 调用teacher的teach方法
teacher.teach();
}
}
-
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
例如:
class Person{
public void say(Person person){
System.out.println("是我说的");
}
}
class Student extends Person{
}
public class Test{
public static void main(String[] args){
Person person = new Person();
Student student = new Student();
person.say(student);
}
}
十三、Object类详解
equals()
-
==和equals的对比
1、 == :既可以判断基本类型,又可以判断引用类型
2、 == :如果判断的是基本类型,则判断的是值是否相等。
3、 == :如果判断的是引用类型,则判断的是内存中的地址是否相等,即判定是不是同一个对象
4、equals:是Object类中的方法,只能判断引用类型
5、默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
// 有些类已经重写了equals方法,变成比较内容了String、Integer
示例源码:
Object类中的equals方法
public boolean equals(Object obj){
return (this == obj);
}
Integer中的equals方法
public boolean equals(Object obj){
if(obj instanceof Integer){
return value == ((Integer)obj).intValue();
}
return false;
} -
如何重写equals方法
/**
* @Description 应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都能一样,则返回true,反之false
* @author cxrg0
*
*/
public class EqualsExperices01{
public static void main(String[] args){
Person p1 = new Person("jack", 10, '男');
Person p2 = new Person("jack", 10, '女');
// System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p2)); // false
}
}
class Person{
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender){
this.name = name;
this.age = age;
this.gender = gender;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
// 类型判断
if(obj instanceof Person){ // 是Person我们才比较
// 向下转型。因为我需要得到obj的各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
// 如果不是Person,直接返回false
return false;
}
}
运行结果:false
Person p1 = new Perosn();
p1.name = "hasp";
Person p2 = new Perosn();
p2.name = "hasp";
System.out.println(p1 == p2); // false
System.out.println(p1.name.equals(p2.name)); // true
System.out.println(p1.equals(p2)); // false
String s1 = new String("asdf");
String s2 = new String("asdf");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
hashCode()
-
java API描述

-
小结
1、提高具有哈希结构的容器的效率
2、两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
3、两个引用,如果指向的是不同对象,则哈希值是不一样的
4、哈希值主要是根据地址号来的!不能完全将哈希值等价于地址
5、后面在集合中hashCode如果需要的话,也会重写
6、例题:测试 A obj = new A(); A obj2 = new A(); A obj3 = new A();的哈希值是否相等
public class EqualsExercise02 {
public static void main(String[] args) {
AA obj = new AA("1");
AA obj2 = new AA("1");
AA obj3 = new AA("2");
System.out.println(obj.hashCode() == obj2.hashCode());
System.out.println(obj == obj2);
System.out.println(obj.name.equals(obj2.name));
System.out.println("");
System.out.println(obj2.hashCode() == obj3.hashCode());
System.out.println(obj2 == obj3);
System.out.println(obj2.name.equals(obj3.name));
System.out.println("");
System.out.println(obj.hashCode() == obj3.hashCode());
System.out.println(obj == obj3);
System.out.println(obj.name.equals(obj3.name));
}
}
class AA{
String name;
public AA(String name){
this.name = name;
}
}
运行结果:

toString()
-
简介
默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息
例如:
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
} -
重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式 -
当直接输出一个对象时,toString方法会被默认的调用
finalize()
1、当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
2、什么时候被回收:当某个对象没有任何引用时,则jvm则认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
3、垃圾回收机制的调用,是由系统决定的,也可以通过System.gc()主动触发垃圾回收机制。
十四、断点调试(Debug)
1、F8 逐行执行
2、F9:跳到下一个断点
3、shift + F8 :跳出当前方法
4、F7 :跳入方法
原文始发于微信公众号(Whisper Coding):Java入门-面向对象
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/255435.html