面向对象
一、初识面向对象
见文件资料的面向对象.md
全内容梳理
类与实体概念
现实生活:
类:抽象的概念,把具有相同特征和操作的事物归为一类
先有实体,再有类的概念
代码世界:
类:抽象的概念,把具有相同属性和方法的对象归为一类
编写顺序:先有类,再创建对象
类的作用:类相当于一个模板,刻画出具有相同属性和方法的对象
类
- 类中只有属性和方法
- 属性也叫做全局变量,属性分为成员变量和静态变量
- 方法分为成员方法和静态方法
public class 类名{//属性也叫做全局变量,分为成员变量和静态变量//成员变量数据类型 变量名;//静态变量static 数据类型 变量名;//方法分为成员方法和静态方法//成员方法访问修饰符 返回值类型 方法名([参数]){}//静态方法访问修饰符 static 返回值类型 方法名([参数]){}
}
对象
创建对象的语法:类名 对象名 = new 类名();
new 构造方法; 属于对象,就在堆内存中开辟空间
类名 对象名 :对象名属于引用,存的是对象在堆内存中的地址
操作对象:
设置成员属性
获取成员属性
调用成员方法
成员属性/成员变量
语法结构:数据类型 变量名;
编写的位置:类里,方法的外面
成员变量 vs 局部变量
成员变量:类里,方法的外面的变量,并且系统会赋默认值,作用在整个类中
局部变量:方法内的变量,系统不会赋默认值,作用在方法中
成员方法
语法结构:访问修饰符 返回值类型 方法名([参数]){}
编写的位置:类里
成员方法 vs 静态方法
成员方法:属于对象的方法,必须使用对象调用
静态方法:属于类的方法,使用类名调用
案例理解
1.初识面向对象的需求
需求:创建人类的对象,并操作对象
分析:
人类 - Person
属性:name、sex、age
方法:eat、sleep
步骤:
1.创建Person类,属性、方法
2.创建Person类的对象
public static void main(String[] args) {//创建对象Person p = new Person();//设置属性p.name = "奇男子";p.sex = '男';p.age = 23;//获取属性System.out.println(p.name);System.out.println(p.sex);System.out.println(p.age);//调用成员方法//注意:成员方法使用引用/对象调用对象p.eat();p.sleep();//调用静态方法//注意:静态方法使用类名调用Person.method();
}
2.关于成员属性/成员变量的ps
注意:属于对象的变量,系统会赋默认值,该类所有的成员方法都能使用该变量
成员变量的默认值:
整数类型:0
浮点类型:0.0
字符类型:’ ’
布尔类型:false
引用类型:null
public class Person {String name;char sex;int age;//成员方法public void eat(){//局部变量:方法里的变量,必须初始化后,才能使用,其作用域在方法内int i = 100;System.out.println("吃饭饭 -- " + i);}public void sleep(){System.out.println("睡觉觉");}//静态方法//注意:静态方法不能使用成员变量public static void method(){System.out.println("静态方法");}}
面向对象理解图、面向对象内存图
见文件画图
1.小结
1.使用到类,JVM就会把该类的class文件加载到方法区,扫描该类的字面值常量,将字面值常量加载到常量池中
2.new对家会在堆中开辟空间,用于存储对象里的成员变量,系统会赋默认值3.引用中存储的是对象在堆中的地址
4.调用方法,就会在栈开辟空间,该空间存放方法的局部变量,方法执行完毕该空间会立刻回收
5.class文件中存放了该类所有的信息(属性、成员方法、静态方法)
初识面相对象深入理解
1.需求:
创建人类的对象,并操作对象
分析:
人类 - Person
属性:name、sex、age
方法:eat、sleep
场景:创建多个对象,去操作对象
public class Person {//成员变量
String name;
char sex;
int age;//成员方法
public void eat(){System.out.println(this.name + "吃饭饭");
}//成员方法
public void sleep(){System.out.println(this.name + "睡觉觉");
}//静态方法
public static void method(){System.out.println("静态方法");
}}
//测试类:该类中有main方法,测试我们写好的代码
public class Test01 {public static void main(String[] args) {Person p1 = new Person();Person p2 = new Person();p1.name = "奇男子";//一个一个赋值p1.sex = '男';p1.age = 22;p2.name = "星宫一花";p2.sex = '女';p2.age = 24;p1.eat();p1.sleep();p2.eat();p2.sleep();Person.method();}}
2.面向对象深入理解图
见文件资料面向对象内存图
小结
1.class文件只加载一次 2.class文件中包含了该类所有的信息
3.new对象会在堆中开辟空间,存放成员变量
4.成员变量系统会赋默认值
5.每个对象独享一份成员变量
6.方法中的this表示调用该方法的对象
7.成员方法可以使用this
8.静态方法使用类名调用,所以方法内不能使用this
二、构造方法
前言 - 创建对象的语法:类型 引用 = new 构造方法;
分类:
无参构造和有参构造
含义:
与类名相同,且没有返回项的方法
作用:
1.和new关键字在一起表示创建对象
2.初始化数据
注意:
1.创建对象的过程(Person p = new Person()😉,也叫做实例化
2.当类中没有有参构造时,系统会默认实现无参构造;反之不会默认实现
3.构造方法可以重载的
有参构造好处:创建对象时,把数据赋值给该对象
经验:
类中编写了有参构造,我们记得把无参构造加上
需求:创建人类,操作对象
public class Person { //成员变量
String name;
char sex;
int age;//构造方法 - 无参构造
public Person(){System.out.println("无参构造");//初始化数据this.name = "默认值";//不写this也是默认使用用thisthis.sex = '男';this.age = 0;
}//构造方法 - 有参构造
public Person(String name,char sex,int age){System.out.println("有参构造");this.name = name;//不写this会报错,将传进来的参数赋值给对象的属性this.sex = sex;this.age = age;
}//成员方法
public void eat(){System.out.println(this.name + "吃饭饭");
}//成员方法
public void sleep(){System.out.println(this.name + "睡觉觉");
}}
public class Test01 {public static void main(String[] args) {/* //无参Person p = new Person();p.name = "奇男子";p.sex = '男';p.age = 22;*///类型 引用 = new 构造方法;Person p = new Person("奇男子", '男', 22);//有参构造方法,new对象时给属性赋值System.out.println(p.name);System.out.println(p.sex);System.out.println(p.age);p.eat();p.sleep();
}}
无参构造:new对象时,成员变量系统会赋默认值,之后会调用无参构造(可以进行初始化数据,测试类中一个一个给属性赋值),构造方法执行完,对象创建成功;
有参构造:new对象时,成员变量系统会赋默认值,之后会调用无参构造,再调用有参构造(可以进行初始化数据,将传进来的参数赋值给对象的属性),构造方法执行完,对象创建成功;
无参构造、有参构造内存图
见文件资料构造方法内存图1、2
三、private
含义:
私有化
private是访问修饰符的一种,访问修饰符规定了访问权限
作用:
1. 修饰属性:私有化属性,不能让外界使用
2. 修饰方法:私有化方法,不能让外界使用
应用场景:
不让让外界访问的属性和方法就用private修饰
案例
public class A {private String str = "用良心做教育";private void method01(){System.out.println("aaabbbccc -- " + str);
}public void method02(){method01();
}}
public class Test01 {public static void main(String[] args) {A a = new A();a.method02();
}
}
注意:
通过公有的方法调用类中私有的方法、属性
四、封装
理解:
不能直接操作属性,可以添加get/set方法
步骤:
1.私有化属性
2.添加get-获取/set-设置方法
好处:
外界不能直接操作属性(有风险),通过get/set方法操作属性,可以在方法内添加额外的功能
需求:
模拟银行用户操作余额的功能
分析:
操作余额(存钱、取钱、查看)
import java.time.LocalDateTime;public class User {String username;
String password;
String name;
String phone;
char sex;
private double surplus;//余额public User() {
}public User(String username, String password, String name, String phone, char sex, double surplus) {this.username = username;this.password = password;this.name = name;this.phone = phone;this.sex = sex;this.surplus = surplus;
}//设置余额
public void setSurplus(double surplus){//额外的功能double num = (surplus - this.surplus);System.out.println(LocalDateTime.now() + " -- " + this.name + "用户操作了金额:" + ((num>0)?"+":"") + num);//设置属性this.surplus = surplus;
}//获取余额
public double getSurplus(){//额外的功能System.out.println(LocalDateTime.now() + " -- " + this.name + "用户获取了金额");//返回余额return surplus;
}}
public class Test01 {public static void main(String[] args) {User user = new User("1234567891", "123123", "奇男子", "17823466612", '男', 2000);//存钱//user.surplus = user.surplus+200;user.setSurplus(user.getSurplus() + 200);//取钱//user.surplus = user.surplus-1800;user.setSurplus(user.getSurplus() -1800);//查看//System.out.println(user.surplus);//400.0System.out.println(user.getSurplus());//400.0
}}
思考题:操作属性有什么功能?
1.设置属性 – set
2.获取属性 – get
面向对象五子棋
见文件代码Gobang02
五、this - 本对象
概念:
表示本对象
理解:哪个对象调用该方法,该方法里的this就表示该对象
含义:
代表本对象,this出现方法里,表示调用该方法的对象
作用:
- this.属性:调用本对象的成员变量
- this.方法:调用本对象的成员方法
- this():调用本对象的构造方法(在一个构造方法中的第一句调用另外一个构造方法)
案例
import java.time.LocalDateTime;public class User {private String username;
private String password;
private String name;
private String phone;
private char sex;
private double surplus;//余额public User() {//在当前构造方法中调用另一个构造方法this("默认账号", "默认密码", "亚当", "12345678901", '男', 0.0);
}public User(String username, String name, String phone, char sex, double surplus) {//在当前构造方法中调用另一个构造方法this(username,"000000", name, phone, sex, surplus);
}public User(String username, String password, String name, String phone, char sex, double surplus) {this.username = username;this.password = password;this.name = name;this.phone = phone;this.sex = sex;this.surplus = surplus;
}//设置余额
public void setSurplus(double surplus){//额外的功能double num = (surplus - this.surplus);System.out.println(LocalDateTime.now() + " -- " + this.name + "用户操作了金额:" + ((num>0)?"+":"") + num);//设置属性this.surplus = surplus;
}//获取余额
public double getSurplus(){//额外的功能System.out.println(LocalDateTime.now() + " -- " + this.name + "用户获取了金额");//返回余额return surplus;
}public String getUsername() {return username;
}public void setUsername(String username) {this.username = username;
}public String getPassword() {return password;
}public void setPassword(String password) {this.password = password;
}public String getName() {return name;
}public void setName(String name) {this.name = name;
}public String getPhone() {return phone;
}public void setPhone(String phone) {this.phone = phone;
}public char getSex() {return sex;
}public void setSex(char sex) {this.sex = sex;
}/*** 转账* @param username 对方的账号* @param money 转账的金额*/public void transferAccounts(String username,double money){//获取本对象的余额,判断是否比转账的金额更多if(this.getSurplus() > money){//将当前用户的余额的money部分转给对方账号//this.setSurplus(this.getSurplus()-money);setSurplus(getSurplus()-money);System.out.println("已经给" + username + "转账成功");}else{System.out.println("余额不足");}}}
经验
私有化属性:eclipse工具栏 ------->Source------->Select getters and setters to create—>勾选需要生成属性的get和set方法;
Source中还有一些自动生成,如构造方法等等;
六、分包
基本作用:防止了类的重名问题
项目作用:项目中有众多的类,把相同功能的类放在同一个包中,方便管理
工具类:com.dream.util/tool
实体类:com.dream.vo/bean/entity/bto/pojo
数据库类:com.dream.dao/mapper
七、static - 静态的
含义:
静态的
关键字
作用:
1.修饰属性
概念区分:
1.成员属性:每个对象独享一份
String str1;
2.静态属性:每个对象共享一份
static String str2;
3.静态属性何时创建?
使用到类,JVM会判断方法区中是否有该类的class文件,如果没有,就把该类的class文件加载到方法区
JVM会扫描该类的所有属性,并把属性添加到静态区中(1.开辟空间 2.赋系统的默认值)
4.静态属性何时销毁?
项目结束时,静态属性才会被销毁,所以静态属性的生命周期很长,项目中使用时需警慎
5.静态属性的应用场景?
该类所有的对象需要共享的属性,就可以设置为静态
静态属性内存图
见文件画图
由于静态属性存在静态区,其属性不允许重复,不同调用赋值会覆盖掉
小结
类加载到方法区时,JVM会扫描该类的所有属性
并把静态属性加载到静态区中,静态属性属于类属性,
该类所有的对象都共享该属性
静态属性直到项目结束时才会被回收 注意:静态属性使用类名调用
A.str2 = "xxx";
//注意:静态属性属于类的属性,直接使用类名调用。使用对象调用会出现警告,但可以运行 //a1.str2 = "xxx";
成员属性对象调用
a1.str1 = "aaa";
2.修饰方法
概念区分
静态方法属于类方法,直接用类名调用
成员方法 vs 静态方法 应用场景
成员方法:必须使用对象调用,也就是说调用成员方法之前必须创建对象(开辟空间)
静态方法:属于类的方法,直接使用类名调用,纯功能的方法就可以设计成静态方法(工具类),比如MyArrays
应用场景:工具类
jar包导入并添加路径,jar包导出,导出API
(1)Java项目打成jar包:选中项目(也可以先点Export再选项目,多个项目导出)---->eclipse的工具的File或右键---->Export------>java–JAR file---->保存路径
【Web项目同理WAR file】
(2)Java项目导入jar包:先选中项目新建一个文件夹(Folder)命名如lib----->将jar所需扔到lib---->再选中改jar右键Build Path选add to Build Path---->就会多出一个第三方jar包文件夹(原来有一个自带的)
导入jar到该项目就可以使用里面的工具类
(3)Java项目导出API:选中项目---->eclipse的工具的File或右键---->Export------>java–Javadoc---->Java comand添加工具路径【ps:C:\Program Files\Java\jdk1.8.0_144\bin\javadoc.exe】---->Destination选保存路径【生成的是一个个的html网页,所以需要选中文件夹,或者在路径后面加一个文件名ps:UtilsAPI,反正没有收纳很乱】
查看API就点击index.html首页进入查看【文档注释才可以显示,注意文档注释和多行注释】
eclipse注释快捷键
单行注释:Ctrl + /【 添加/消除//注释 】
多行注释: Ctrl+Shift+/ 【 添加/**/注释 】Ctrl+Shift+\【取消/* */注释】
文档注释: ALT + SHIFT +J 或者多行注释再加个*号
在类上面(直接按ALT + SHIFT +J )
/*** @author 奇男子 * */
方法上面的(鼠标焦点放在方法里,然后 ALT + SHIFT +J )
/**
- 删除学生
- @param classId 目标班级号
- @param id 目标学号
- @return 删除状态码
*/
public int delete(String classId,String id);
需求:
模仿Java的MyArrays,编写自己的MyMyArrays
目的:
1.掌握工具类的概念
2.回顾之前的知识点
3.理解文档注释的作用
代码见Utils
3.静态代码块
概念区分
静态代码块是类加载到方法区时才会被调用,该代码块只能初始化静态变量
代码块是创建对象时优先于构造方法调用,该代码块可以初始化成员变量和静态变量
构造方法是创建对象时调用,该方法可以初始化成员变量和静态变量
成员变量 vs 静态变量
成员变量:属于对象的变量,每个对象独享一份
静态变量:属于类的变量,每个对象都共享一份
public class A {String str1;//成员属性
static String str2;//静态属性//静态代码块:class文件加载到方法区时调用
//作用:操作静态属性,不能操作成员属性
static{A.str2 = "eee";//底层实现:A.str2 = "eee";System.out.println("静态代码块 --" + A.str2);
}//代码块:创建对象时优先于构造方法调用
//作用:操作成员属性和静态属性
{str1 = "ccc";//底层实现:this.str1 = "ccc";str2 = "ddd";//底层实现:A.str2 = "ddd";System.out.println("代码块-- " + str1 + " -- " + str2);
}//构造方法:创建对象时调用构造方法
//作用:操作成员属性和静态属性
public A() {str1 = "aaa";//底层实现:this.str1 = "aaa";str2 = "bbb";//底层实现:A.str2 = "bbb";System.out.println("构造方法 -- " + str1 + " -- " + str2);
}
}
经验:
1.创建对象时,在构造方法中初始化数据
2.项目中可以在静态代码块中初始化静态属性
八、类加载机制
类加载机制内存图
见文件画图
加载过程
面试题
1.面试题1输出结果
public class A {static A a = new A();static int value1;static int value2 = 0;public A() {value1++;value2++;}}
public class Test01 {public static void main(String[] args) {System.out.println(A.value1);//1System.out.println(A.value2);//0
}
}
准备阶段1:JVM给静态属性在静态区中开辟空间
//A a;
//int value1;
//int value2;准备阶段2:JVM给静态属性赋系统默认值
//A a = null;
//int value1 = 0;
//int value2 = 0;初始化阶段:java程序代码才开始真正执行
//A a = new A();
//int value1 = 1;
//int value2 = 0;new对象调用无参构造方法value1和value2都得1,然后执行赋值语句value1不变,value2赋值为0
2.面试题1输出结果
public class A {static int value1;static int value2 = 0;static A a = new A();public A() {value1++;value2++;}}
public class Test01 {public static void main(String[] args) {System.out.println(A.value1);//1System.out.println(A.value2);//1
}
}
准备阶段1:JVM给静态属性在静态区中开辟空间
//int value1;
//int value2;
//A a;准备阶段2:JVM给静态属性赋系统默认值
//int value1 = 0;
//int value2 = 0;//0是系统赋的默认值
//A a = null;初始化阶段:java程序代码才开始真正执行
//int value1 = 1;
//int value2 = 1;
//A a = new A();先执行赋值语句value1不改变系统默认赋值的0,value2被语句赋值为0;再new对象调用无参构造方法value1和value2都得1
深入类加载机制
见文件资料的深入类加载机制.md
九、继承
概念
Java中的继承是一个对象获取父对象的所有属性和行为的机制
理解:继承是指一个类(子类)可以继承另一个类(父类)的属性和方法
关键字extends
优点:减少代码的冗余
缺点:继承会增加类与类之间的关系,会增加代码的维护难度
继承的使用
使用场景:多个类似的类,有相同的属性和方法,就可以把相同属性和方法抽取到父类
需求:编写中国人和日本人的类,创建各自的对象
分析:
人类:
属性:姓名、性别、年龄
方法:吃饭饭、睡觉觉
中国人的类 继承 人类 :
属性:身份证
方法:打太极
日本人的类 继承 人类:
属性:年号
方法:拍电影
ps:(省略部分代码)
public class Person {String name;
char sex;
int age;public void eat(){System.out.println(this.name + "吃饭饭");
}public void sleep(){System.out.println(this.name + "睡觉觉");
}}
// 子类 继承 父类
public class Chinese extends Person{String id;public void playTaiJi(){System.out.println("中国人打太极");
}}
//创建对象Chinese c = new Chinese();//操作父类属性c.name = "奇男子";c.sex = '男';c.age = 22;System.out.println(c.name);System.out.println(c.sex);System.out.println(c.age);//操作子类属性c.id = "1234567890";System.out.println(c.id);//调用父类方法c.eat();c.sleep();//调用子类方法c.playTaiJi();
继承内存图
见文件画图
super
理解:
super表示父类
作用:
1.super.属性:在子类中,调用父类的非私有化的成员属性
2.super.方法:在子类中,调用父类的非私有化的成员方法
3.super():在子类构造方法中的第一句调用父类的非私有化的构造方法
案例:
继承的使用的需求案例
封装父类,添加无参构造、有参构造、get、set方法(都由eclipse直接生成),成员方法保留
经验
私有化属性:eclipse工具栏 ------->Source------->Select getters and setters to create—>勾选需要生成属性的get和set方法;
Source中还有一些自动生成,如构造方法等等;
子类继承父类,封装子类,super使用
public class Chinese extends Person{private String id;public Chinese() {
}public Chinese(String name, char sex, int age, String id) {super(name, sex, age);this.id = id;
}public String getId() {return id;
}public void setId(String id) {this.id = id;
}public void playTaiJi(){System.out.println(super.getName() + "打太极");
}}
测试类,有有参构造直接赋值,不用一个个赋值
//创建对象Chinese c = new Chinese("奇男子", '男', 22, "1234567890");//调用父类方法c.eat();c.sleep();//调用子类方法c.playTaiJi();
小结
编写一个类的步骤:
1.属性
2.私有化属性
3.无参构造
4.有参构造
5.get/set方法
6.其他的方法
深入继承:
思考题:
创建子类对象,会不会调用父类构造方法?
会创建子类对象,会不会创建父类对象?
不会创建子类对象,为什么会调用父类构造方法?
目的是将父类的属性存放在子类对象中(因为会在子类对象中开辟空间,用于存储父类的成员属性)创建子类对象,先调用父类构造方法还是子类构造方法?
先调用子类构造方法创建子类对象,先完成父类构造方法还是子类构造方法?
先完成父类构造方法子类可以继承父类私有化的属性和方法吗?
可以,但是只能间接调用
- 子类对象是否能继承父类所有的属性和方法?
Java官网上,明确表示子类不能继承父类私有化的属性和方法,这是站在使用的角度
实际上,子类能继承父类私有化的属性方法,但是不能直接使用,可以在父类中编写公有方法去调用私有的属性和方法
案例:
public class Father {private String fatherAttr = "父类私有化属性";public Father() {System.out.println("调用父类的构造方法");
}public String getFatherAttr() {return fatherAttr;
}public void setFatherAttr(String fatherAttr) {this.fatherAttr = fatherAttr;
}private void method01(){System.out.println("父类私有化方法");
}public void method02(){method01();
}}
public class Son extends Father{public Son() {//super();//默认实现:调用父类的构造方法System.out.println("调用子类的构造方法");
}}
public class Test01 {public static void main(String[] args) { Son son = new Son();System.out.println(son.getFatherAttr());son.method02();
}}
重写/复写
含义:
重写也叫做复写,将父类中方法在子类中重新编写一遍
应用场景:
父类方法不满足子类需求时,子类就可以重复父类的方法
条件:
1.在子类中重写父类的方法
2.返回值、方法名、参数列表必须跟父类重写的方法一致
3.访问修饰符不能比父类更严格
需求:
编写中国人和日本人的类,创建各自的对象
public class Chinese extends Person{
//省略一部分//@Override -- 重写的注解
//注解:给程序员和系统解释代码信息
@Override
public void eat(){System.out.println(super.getName() + "吃山珍海味");
}
//在测试类new对象调用输出
十、访问修饰符
含义:
修饰类、方法、属性,定义使用的范围
理解:给类、方法、属性定义访问权限的关键字
注意:
1.修饰类只能使用public和默认的访问权限
2.修饰方法和属性可以使用所有的访问权限
访问修饰符 | 本类 | 本包 | 其他包子类 | 其他包 |
---|---|---|---|---|
private | OK | |||
默认的 | OK | OK | ||
protected | OK | OK | OK | |
public | Ok | OK | OK | Ok |
做实验
两个包下分别父类和子类,测试类进行实验
经验:
1.属性一般使用private修饰,因为封装
2.属性或者方法如果需要被子类使用,一般使用protected修饰
3.方法如果不需要被外界使用,一般使用private修饰
4.方法一般使用public修饰
十一、Object
概念:
所有类的基类或者超类
理解:如果一个类没有明确继承的类,默认继承Object,所以说Object是所有类的祖先类
方法
equals:
作用:比较两个对象内存地址是否相同
public boolean equals(Object obj){return this == obj;}
注意:equals和==
a.基本数据类型:==比较的是值是否相同
b.引用数据类型:==比较的是内存地址
查看equals底层
鼠标放在equals上按ctrl点击----->点击Attach Source------>选择外部路径External location----->ps:C:\Program Files\Java\jdk1.8.0_144\src.zip
equals内存图
见文件画图
扩展
1.Java方法的参数是值传递
2.如果方法中参数是基本类型直接将实参的值传入该方法 3.如果方法中的参数是引用类型将对象的内存地址作为值传入该广法
hashCode:
作用:获取对象的hash值
hash值不等于内存地址,hash值是对象的内存地址+散列算法,算出来的一个数值
System.out.println(obj1.hashCode());//366712642
getClass:
作用:获取类的字节码文件对象
注意:
使用到类,会把该类的class文件加载到方法区,并且在堆中创建该类的class对象
该类的class对象作为class文件的访问入口
class文件只加载1次,所以该类的class对象在内存中也只有一个
Object obj1 = new Object();//0x001Object obj2 = new Object();//0x002Class<? extends Object> class1 = obj1.getClass();//获取的是Object类的class对象Class<? extends Object> class2 = obj2.getClass();//获取的是Object类的class对象System.out.println(class1 == class2);//true
toString:
作用:获取对象的字符串表示(将对象转换为字符串)
toString底层:
public String toString(){
//java.lang.Object @ 15db9742
return getClass().getName() + “@” + Integer.toHexString(hashCode());//getClass().getName() - class对象的全路径(报名+类名)//Integer.toHexString(hashCode()) - 获取对象的十六进制hash值
}
Object obj1 = new Object();//0x001Object obj2 = new Object();//0x002System.out.println(obj1.toString());//java.lang.Object@15db9742System.out.println(obj2.toString());//java.lang.Object@6d06d69c
方法的使用
Object方法标杆作用
Object方法标杆作用:Object作为所有类的父类,定义了几个方法,方便子类去重写
equals:比较两个对象是否相同,不同子类比较两个对象的规则不一样,所以子类重写即可
toString:每个子类都有不同的属性,重写toString直接打印该对象中所有的属性,方便观察数据
getClass():在反射的知识点中使用
hashCode():在集合的知识点中使用
equals和toString使用
需求:编写用户类,比较两个对象是否相同,并打印
封装User(默认继承Object),重写equals、toString
@Overridepublic boolean equals(Object obj) {if(this == obj){//比较两个对象的内存地址是否一致return true;}//两个对象的内存地址不一致User use = (User) obj;//需要强转,java的参数是值传递//当前对象的账号和传进来对象的账号,此处是String类型的equals()if(this.username.equals(use.username)){return true;}return false;}@Overridepublic String toString() {//打印所有属性return this.username + " -- " + this.password + " -- " + this.nickName + " -- " + this.name;}//测试类User user1 = new User("1445584980", "123123", "撕裂的忧伤", "彭于晏");//0x001User user2 = new User("1445584980", "123123", "撕裂的忧伤", "彭于晏");//0x002System.out.println(user1.equals(user2));System.out.println(user1.toString());System.out.println(user2.toString());
深入String的equals()
MyString
重写方法(@Override),String的equals底层原理
首先toCharArray()将字符串转换为字符数组
重写equals
a.比较内存地址是否相同
b.比较字符长度是否相同
c.比较字符的Unicode码是否相同
重写toString
valueOf()将字符数组转为字符串
注意:打印对象,默认调用toString()
public class MyString {private char[] value;public MyString(String original) {//"abc"//['a','b','c']value = original.toCharArray();//将字符串转换为字符数组
}@Override
public boolean equals(Object obj) {if(this == obj){return true;}MyString my = (MyString) obj;char[] v1 = this.value;char[] v2 = my.value;//比较字符长度if(v1.length != v2.length){return false;}for (int i = 0; i < v1.length; i++) {//比较字符的Unicode码是否相同if(v1[i] != v2[i]){return false;}}return true;
}@Override
public String toString() {return String.valueOf(value);//将字符数组转为字符串
}}
2.面试题:String是如何比较两个字符串是否相同的
1.比较两个字符串的地址是否相同
2.比较两个字符串的字符个数
3.循环比较字符的Unicode码
//String和MyString都可以实现,表明重写的MyString符合要求
String str1 = new String("abc");String str2 = new String("abc");System.out.println(str1 == str2);//falseSystem.out.println(str1.equals(str2));//trueSystem.out.println(str1);System.out.println(str2);System.out.println("-------------------------------");MyString m1 = new MyString("abc");MyString m2 = new MyString("abc");System.out.println(m1 == m2);//falseSystem.out.println(m1.equals(m2));//true//注意:打印对象,默认调用toString()System.out.println(m1);System.out.println(m2);
十二、final
含义:
最终的
关键字
作用:
- 修饰类:该类不能被继承
- 修饰方法:该方法不能被重写
- 修饰变量:变成常量,不能重新赋值
注意:
1.常量在常量池中声明,项目结束时才会被销毁
2.常量的命名规范:全部单词大写,单词之间使用下划线隔开
3.常量的生命周期:存在常量池中,直到项目结束才会被销毁
经验:
如果不想让该类有子类(不想该类有扩展),就是用final修饰final和static一般一起修饰属性,该属性变成静态常量
做实验
//final修饰类,该类不能被继承
//public final class A {
public class A {//final修饰方法,该方法不能被子类重写
//public final void method(){
public void method(){System.out.println("父类的方法");
}}
public class B extends A{@Override
public void method() {System.out.println("子类重写父类的方法");//final修饰变量,变量变为常量,不能重新赋值final int i = 100;System.out.println(i);
}}
十三、抽象类及抽象方法
//抽象类
public abstract class 类名{//抽象方法public abstract void method();
}
abstract
关键字,用来修饰类和方法
不能与final,static,private一起修饰,不能被重写
抽象类
1.概念:
被abstract修饰的类,包含抽象方法的类就是抽象类
2.抽象类的特征:
- 抽象类不能实例化,即不能用new来实例化抽象类
- 抽象类中包含有构造方法,但构造方法不能用来new实例,只能用来被子类调用
- 抽象类中可以包含成员变量,成员方法,静态方法,构造方法,final 修饰的方法,抽象方法
- 抽象类只能用来被继承
3.应用场景:
当一个方法必须在父类中出现,但是这个方法又不好实现,就把该方法变成抽象方法,交给非抽象的子类去实现
抽象方法
1.概念
使用abstract修饰的方法,没有方法体,只有声明,交给非抽象的子类去实现(重写)
理解:
将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,在父类中不能确定具体的方法体。 即正向实现反向抽取。
2.抽象方法的特征:
- 抽象类中可以包含 0 个或多个抽象方法
- 抽象方法必须被子类实现
- 抽象类中可以有属性、构造方法、成员方法、静态方法、抽象方法
- 有抽象方法的类只能定义成抽象类(含有抽象方法的类必须是抽象类)
- 只有当子类实现了抽象超类中的所有抽象方法,子类才不是抽象类,才能产生实例
- 如果子类中仍有抽象方法未实现,则子类也只能是抽象类
抽象类及抽象方法的使用
需求:
编写人类为父类,编写两个子类(ps:中国人、日本人),中国人的子类(ps:四川人、广东人)
封装Person类并为抽象类,写一个抽象方法eat()
public abstract void eat();
Japanese类继承Person类,重写eat()
public class Japanese extends Person{private String yearNum;public Japanese() {
}public Japanese(String name, char sex, int age, String yearNum) {super(name, sex, age);this.yearNum = yearNum;
}public String getYearNum() {return yearNum;
}public void setYearNum(String yearNum) {this.yearNum = yearNum;
}@Override
public void eat() {System.out.println(super.getName() + "吃生鱼片");
}}
Chinese类继承Person类,但是并没有实现eat(),所以Chinese类为抽象类,交给他的子类去实现。这里扩展添加了一个抽象方法hobbies()
public abstract class Chinese extends Person{private String id;public Chinese() {
}public Chinese(String name, char sex, int age, String id) {super(name, sex, age);this.id = id;
}public String getId() {return id;
}public void setId(String id) {this.id = id;
}public abstract void hobbies();}
SiChuan类继承Chinese类,去实现了eat(),他可以实例化
public class SiChuan extends Chinese{public SiChuan() {
}public SiChuan(String name,char sex,int age,String id){super(name, sex, age, id);
}@Override
public void hobbies() {System.out.println(super.getName() + "喜欢打麻将、炸金花");
}@Override
public void eat() {System.out.println(super.getName() + "吃火锅、串串香");
}}
抽象类及抽象方法的深入
面试题:
1.抽象类不能有构造方法?
抽象类可以有构造方法
2.抽象类中只能有抽象方法?
抽象类中有非抽象方法(成员方法和静态方法)
3.抽象类中不可以没有抽象方法?
抽象类中可以没有抽象方法,但是毫无意义
4.如果父类是抽象类,则子类必须实现父类的抽象方法?
不一定,子类如果是抽象类,可以不实现父类的抽象方法
5.可以使用new关键字来创建抽象类对象?
不可以,创建的是匿名内部类的对象
抽象类及抽象方法的使用的扩展
匿名内部类
匿名内部类理解图见文件画图
I1 i1 = new Person() {@Overridepublic void i1Method() {System.out.println("用良心做教育");}};
在需要创建一个临时对象来实现特定抽象类的情况下,可以直接通过匿名内部类来完成。这样不必单独编写一个新的类文件,节省了开发工作量。
public class Test02 {public static void main(String[] args) {Person p = new Person("弗罗兹·甘地",'男',23) {//继承父类属性@Overridepublic void eat() {System.out.println(super.getName() + "吃咖喱");}};p.eat();}
}
接口
1.概念:
Java中一种引用类型,是方法的集合
特殊的抽象类
关键字interface
理解:
- 特殊的抽象类
- JDK1.7时,接口中只能有静态常量和抽象方法
- JDK1.8开始,接口中添加了静态方法和默认方法
注意:
1.接口中的抽象方法默认添加public abstract(经验:一般把abstract去掉)
2.接口中的属性默认添加public static final
3.接口中的默认方法默认添加public
2.应用场景:
接口相当于是制定规则(标准),再让实现类去实现
抽象类 vs 接口
抽象类:成员变量、静态变量、静态常量、成员方法、静态方法
接口:静态常量、静态方法(JDK1.8)、默认方法(JDK1.8)
3.类 - 接口的关系:
类 - 类:单继承(一个类只能继承另一个类,不能继承多个类)
类 - 接口:多实现(一个类可以实现多个接口)
接口 - 接口:多继承(一个接口可以继承多个接口)
做代码实验验证
4.实现
关键字implements,子类实现接口
需求:设计学生管理系统项目的接口
分析:
学生管理系统管理一个一个的学生对象
管理 - 数据的操作:增、删、改、查
(1)封装一个学生类,里面重写toString()
(2)写一个学生管理系统的接口
public interface IStudentManagerSystem { //静态常量
//默认使用public static final修饰
int NAME = 1;
int SEX = 2;
int AGE = 3;
int CLASS_ID = 4;
int ID = 5;//抽象方法
//默认使用public abstract修饰
public void add(Student stu);public void delete(String classId,String id);public void update(String classId,String id,int type,Object val);public Student getStu(String classId,String id);//静态方法
public static void method01(){System.out.println("IStudentManagerSystem接口中的静态方法");
}//默认方法
//默认使用public修饰
default void method02(){System.out.println("IStudentManagerSystem接口中的默认方法");
}
}
(3)写学生管理系统的实现类实现接口生管理系统,当写完类名时就会报红提示实现接口里的抽象方法,点击就会自动需要实现的方法,之后去完善就可以了
public class StudentManagerSystemImpl implements IStudentManagerSystem{@Override
public void add(Student stu) {
}@Override
public void delete(String classId, String id) {
}@Override
public void update(String classId, String id, int type, Object val) {
}@Override
public Student getStu(String classId, String id) {return null;
}}
(4)测试类调用方法
StudentManagerSystemImpl sms = new StudentManagerSystemImpl();//调用实现类实现的方法sms.add(new Student());//调用默认方法sms.method02();//调用静态方法,类名调用IStudentManagerSystem.method01();
}
5.面试题:
1.一个类可以实现多个接口?
可以
2.一个接口可以实现多个接口?不可以,接口与接口的关系是多继承
3.接口里面的方法不一定都是抽象的?是的,因为JDK1.7时接口里只能有抽象方法,JDK1.8时接口可以有抽象方法和默认方法
4.接口解决了类的单继承问题?是的,因为类与类是单继承,类与接口是多实现
5.一个类是否可以继承一个类并同时实现多个接口?可以
6.接口可以new对象?不可以,因为接口是特殊的抽象类,但是本质还是抽象类,抽象类是不可以new对象的,接口也是不能new对象,new出来的匿名类内部类的对象
6.接口的使用扩展
(匿名内部类)匿名实现类
匿名内部类理解图见文件画图
I1 i1 = new I1() {@Overridepublic void i1Method() {System.out.println("用良心做教育");}};
十四、多态
概念
多态性是指同一操作或方法可以在不同的对象上具有不同的行为。它允许我们通过使用基类或接口类型的引用变量来调用子类或实现类的方法。
理解:多种形态
分类:
类的多态:子类对象指向父类引用(父类引用中存储的是子类对象在堆中开辟的地址)
接口的多态:实现类对象指向接口的引用(接口的引用中存储的是实现类对象在堆中开辟的地址)
设计
设计原则:前人总结的经验,告诉我们什么不该做
设计模式:前人总结的经验,告诉我们一步一步的怎么做
ps:OCP原则:
O - Open - 在需求升级时,对于创建类是欢迎的
(因为创建类对于原来代码的影响几乎为0)
C - Close - 在需求升级时,对于改动原有类是拒绝的
(因为原有类之间的关系是趋于稳定状态,如果改动原有类,
很有可能打破这种平衡,导致bug的出现)
P - Principle - 原则
优缺点:
优点:提高程序的维护性,在需求升级/迭代时,不违反OCP原则
缺点:不能调用子类独有的属性和方法
类的多态:
1.概念:
子类对象指向父类引用
理解:父类引用中存储的是子类对象在堆中开辟的内存地址
2.需求
需求:使用代码描述出老师骑着自行车上班
分析:老师类、自行车类步骤:
1.创建Bick类,编写open、close
2.创建Teacher,编写start、stop
Teacher类start、stop方法里面调用Bick类的start、stop方法,操作具体的车
需求升级:自行车 -> 小汽车
步骤:
1.创建Car类,编写open、close
2.改动原来的Teacher,编写start、stop同理Teacher类重载start、stop方法
需求升级:自行车 -> 小汽车 -> 飞机
步骤:
1.创建Vehicles类,编写抽象方法open、close
2.创建Plane类,改动Bick、Car类继承Vehicles,重写open、close
3.改动原来的Teacher,改动start、stop操作交通工具
测试类:
Teacher t = new Teacher();//类的多态:子类对象指向父类引用//理解:父类引用中存储的是子类对象在堆中开辟的内存地址Vehicles v = new Plane();t.start(v);System.out.println("欣赏沿途的风景");t.stop(v);
接口的多态
1.概念
实现类对象指向接口的引用
理解:接口的引用存储的是实现类对象在堆中开辟的地址
2.需求:
使用代码描述出电脑连接外部设备
分析:电脑类、USB接口、鼠标类、硬盘类、键盘类
步骤:
1.创建IUSB接口,抽象方法use
2.创建鼠标类Mouse、硬盘类Disk、键盘类KeyBoard、都实现接口,并且都重写抽象方法use;
3.电脑类Computer写一个连接方法connection操作接口
测试类:
Computer computer = new Computer();//接口的多态:实现类对象指向接口的引用//理解:接口的引用存储的是实现类对象在堆中开辟的地址IUSB usb = new KeyBoard();computer.connection(usb);
}
十五、对象转型
前提:有继承关系
自动转型 - 向上转型:
子类类型 转 父类类型
注意:
向上转型就是多态!!!
2. 向上转型后,可以调用父类属性、方法 3. 向上转型后,不可以调用子类独有的属性、方法 4. 向上转型后,可以调用子类重写父类的方法
需求:父类,子类
写一个Father类、一个Son类继承Father
测试类进行验证
//向上转型Father father = new Son();System.out.println(father.fatherAttr);father.fatherMethod();
强制转型 - 向下转型:
父类类型 转 子类类型
注意:
- 向下转型是不安全的 – ClassCastException类型转换异常
- 出现ClassCastException,一定要看错误信息
- 父类对象不能赋值给子类引用 – Dog dog = (Dog) new Animal();
- 向下转型之前必须先向上转型
- 向下转型之前,使用instanceof判断类型
ps:MyString
if(obj instanceof MyString){MyString my = (MyString) obj;}
需求:动物类,猫类,狗类
创建动物类Animal;创建猫类Cat、狗Dog继承Animal
测试类验证:
//前提:向上转型Animal an = new Cat();//向下转型if(an instanceof Dog){//判断引用an中指向的对象是否是Dog类型Dog dog = (Dog) an;dog.shout();}else if(an instanceof Cat){//判断引用an中指向的对象是否是Cat类型Cat cat = (Cat) an;cat.eat();}
应用场景
对象转型的应用包括但不限于以下几个方面:
1.多态性的实现:Java中的多态性是通过对象转型来实现的。通过向上转型,将子类对象赋值给父类类型的变量,可以实现对多个子类对象的统一处理,从而实现多态性。
2.接口的实现:接口是Java中重要的面向对象编程概念,对象转型可以用于实现接口。将一个实现了某个接口的类类型对象转换为该接口类型,就可以通过接口类型调用实现类中实现的方法。
3.继承关系的处理:Java中的继承关系是通过对象转型来实现的。将一个子类类型的对象转换为一个父类类型的对象,就可以实现父类和子类之间的继承关系。
总之,Java中的对象转型是java面向对象编程的重要特性之一,可以帮助程序员更好地实现面向对象编程的思想,提高代码的可复用性和可维护性。案例:MyString(参考day15)
更新MyString(代码,向下转型之前,使用instanceof判断类型
if(obj instanceof MyString){MyString my = (MyString) obj;
测试类:测试MyString达到要求
String str1 = new String("abc");System.out.println(str1.equals(new Student()));//false System.out.println("-------------------------------");MyString m1 = new MyString("abc");System.out.println(m1.equals(new Student()));//false
十六、内部类
理解:
一个类中再声明另外一个类
应用场景:
- B类的对象只在A类中使用,并且B类对象使用到了A类所有的属性,就可以将B类作为A类的成员内部类
- B类的对象只在A类中使用,并且B类对象使用到了A类静态的属性,就可以将B类作为A类的静态内部类
- 抽象类的子类只创建一次对象,就没必要创建子类,直接使用匿名内部类(new 抽象类)
- 接口的实现类只创建一次对象,就没必要创建实现类,直接使用匿名内部类(new 接口)
分类:
成员内部类
静态内部类
接口内部类
局部内部类
匿名内部类
//外部类
public class Outter {//成员内部类class Inner01{}//静态内部类static class Inner02{}public void method(){//局部内部类class Inner03{} }
}
//接口
public interface I1 {//接口内部类class Inner{}
}
成员内部类
需求:创建成员内部类的对象,操作对象的方法
注意:
创建成员内部类对象之前,必须创建外部类对象
成员内部类可以调用外部类所有的属性
在成员内部类中调用指定的外部类属性:外部类.this.属性
ps:测试类
//创建成员内部类的对象Inner inner = new Outter().new Inner();//调用方法inner.innerMethod();
//外部类
public class Outter {private String str1 = "外部类属性1";String str2 = "外部类属性2";
protected String str3 = "外部类属性3";
public String str4 = "外部类属性4";
final String str5 = "外部类属性5";
static String str6 = "外部类属性6";
static final String str7 = "外部类属性7";//成员内部类
class Inner{String str1 = "内部类属性";public void innerMethod(){System.out.println("成员内部类的方法");System.out.println(str1);//this.str1,就近原则,调内部类属性System.out.println(Outter.this.str1);//调外部类属性System.out.println(str2);//Outter.this.str2System.out.println(str3);//Outter.this.str3System.out.println(str4);//Outter.this.str4System.out.println(str5);//Outter.this.str5System.out.println(str6);//Outter.str6,调外部类静态属性,外部类名调用System.out.println(str7);//Outter.str7}
}
}
静态内部类
需求:创建静态内部类的对象,操作对象的方法
注意:
- 创建静态内部类对象,不用创建外部类对象
- 静态内部类只能调用外部类静态的属性
测试类
//创建静态内部类的对象Inner inner = new Outter.Inner();//调用方法inner.innerMethod();
//外部类
public class Outter { static String str1 = "外部类属性1";
static final String str2 = "外部类属性2";//静态内部类
static class Inner{public void innerMethod(){System.out.println("静态内部类的方法");System.out.println(str1);//Outter.str1System.out.println(str2);//Outter.str2}
}
}
接口内部类
需求:创建接口内部类的对象,操作对象的方法
注意:
- 接口内部类默认使用public static修饰
- 接口内部类的使用方式和静态内部类一致
测试类
//创建静态内部类的对象Inner inner = new Outter.Inner();//调用方法inner.innerMethod();
//外部接口
public interface Outter {//接口内部类//默认使用public static修饰class Inner{public void innerMethod(){System.out.println("接口内部类的方法");}}}
局部内部类
1.使用
需求:调用局部内部类的方法
注意:
- 局部内部类不能使用访问修饰符
- 局部内部类的作用域就在外部类方法中
- 常量:存放在常量池中,项目销毁时,常量才会被回收;局部变量:调用方法,方法在栈中开辟空间,用于存放局部变量,方法执行完毕,该空间会立刻回收
测试类:
Outter outter = new Outter();outter.method();
//外部类
public class Outter { public void method(){ //局部内部类class Inner{public void innerMethod(){System.out.println("局部内部类的方法");}}//创建局部内部类对象Inner inner = new Inner();//调用方法inner.innerMethod(); }
}
2.局部内部类理解图
见文件画图
3.面试题:
局部内部类使用到外部类的局部变量时,为什么局部变量会变为常量?
答:局部变量变成常量,是让该变量的生命周期变长,是让方法以外还能找的到该数据,如果该变量是局部变量,方法执行完毕就直接被回收,在方法就不能使用该数据
匿名内部类
匿名内部类理解图
见文件画图匿名内部类理解图*2
应用场景:
作为接口或者抽象类的实现类:在需要创建一个临时对象来实现特定接口或者抽象类的情况下,可以直接通过匿名内部类来完成。这样不必单独编写一个新的类文件,节省了开发工作量。
创建匿名内部类的对象
(匿名子类,父类)
- 底层创建一个匿名类(Test01$1.class),继承A类,重写method方法
- 创建匿名子类的对象
- 赋值给父类的引用(类的多态)
【Test01$1.class是系统默认命名,通过查看class文件就可以看到;eclipse里查看:的工具栏---->window—>showview---->Navigator—显示bin目录的class文件】
(匿名子类实现类,父类接口)
- 底层创建一个匿名类(Test01$1.class),实现I1接口,重写method方法
- 创建匿名实现类的对象
- 赋值给接口的引用(接口的多态)