面向对象编程(中级)
IDEA 使用
快捷键
File -> Settings -> Keymap
模板
FIle -> Settings -> Editor -> Live Templates
包
包的作用
- 区分相同名字的类
- 当类很多时,包可以很好地管理类
- 控制访问范围
包的基本语法
package com.hspedu
package
关键字,表示打包com.hspedu
表示包名
包的本质分析(原理)
包的本质就是创建不同的文件夹(目录)来保存类文件
快速入门
命名规则
只能包含数字、字母、下划线、小圆点,但不能以数字开头,不能是关键字或保留字
命名规范
一般是小写字母 + 小圆点
com.公司名.项目名.业务模块名
常用的包
java.lang.*
// lang 包是基本包,默认引入的,不需要再引入
java.util.*
// util 包,系统提供的工具包,工具类,使用 Scanner
java.net.*
// 网络包,网络并发
java.awt.*
// 做 java 界面开发,GUI
细节
我们引入一个包的主要目的是要使用该包下的类
比如 import java.util.Scanner ,就只是引入一个类Scanner
import java.util.* ,表示将 java.util 包所有引入
建议:需要使用哪个类就导入哪个类即可,不建议使用 * 导入
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多有一句 package
- import 指令,放在 package 的下面,在类定义前面,可以有多句且没有顺序要求
访问修饰符(modifier)
基本介绍
java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
- 公开级别:用
public
修饰,对外公开 - 受保护级别:用
protected
修饰,对子类和同一个包中的类公开 - 默认级别:没有修饰符号,向同一个包的类公开
- 私有级别:用
private
修饰,只有类本身可以访问,不对外公开
细节
- 修饰符可以用来修饰类中的属性、成员方法以及类
- 只有默认修饰符和 public 才能修饰类
- 子类后面再讨论
- 成员方法的访问规则和属性完全一样
*封装(encapsulation)
封装是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过授权的操作(方法),才能对数据进行操作。
封装的好处
- 隐藏实现细节 方法 <-- 调用
- 可以对数据进行验证,保证安全合理
实现步骤
- 将属性私有化 private【不能直接修改属性】
提供一个公共的 public set 方法,用于对属性判断并赋值
public void setXxx(参数列表){
// 加入数据验证的业务逻辑
属性 = 参数名;
}
提供一个公共的 public get 方法,用于获取属性的值
public 数据类型 getXxx(){
// 权限判断,Xxx 某个属性
return xx;
}
快速入门
package com.bbedu.encap;
public class Encapsulation01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("Jack");
person.setAge(30);
person.setSalary(30000);
System.out.println(person.info());
Person tim = new Person("Tim", 2000, 20000);
System.out.println("======Tim's information======\n" + tim.info());
}
}
class Person {
public String name; // 名字公开
private int age; // 年龄私有化
private double salary;
// 与构造器结合
public Person() {
}
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
// 我们可以将set方法写在构造器中,这样仍然可以验证
setName(name);
setAge(age);
setSalary(salary);
}
// 自动 set get, alt + insert
public String getName() {
return name;
}
public void setName(String name) {
// 加入对数据的校验
if(name.length() >= 2 && name.length() <= 6){
this.name = name;
}else{
System.out.println("名字长度有误,需要(2-6)字符,默认佚名");
this.name = "佚名";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
// 判断
if(age >= 1 && age <= 120){
this.age = age;
}else{
System.out.println("年龄输入有误,要在1-120岁,默认为18");
this.age = 18; // 默认年龄
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
// 可以增加当前对象的权限判断
this.salary = salary;
}
// 写一个方法,返回属性信息
public String info(){
return "信息为 name=" + name + " age=" + age
+ " salary=" + salary;
}
}
练习
Account.java
package com.bbedu.encap;
public class Account {
// 三个属性设为 private
private String name;
private double balance;
private String pwd;
// 提供两个构造器
public Account(){
}
public Account(String name, double balance, String pwd) {
this.setName(name);
this.setBalance(balance);
this.setPwd(pwd);
}
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 4){
this.name = name;
}else{
System.out.println("姓名输入错误,应为(2-4位),默认为佚名");
this.name = "佚名";
}
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
if(balance > 20){
this.balance = balance;
}else{
System.out.println("余额必须(>20), 默认位0");
this.balance = 0;
}
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
if(pwd.length() == 6){
this.pwd = pwd;
}else {
System.out.println("密码必须是(6位), 默认为000000");
this.pwd = "000000";
}
}
//显示账号信息
public void showInfo() {
System.out.println("账号信息 name="+ name + " 余额=" + balance + "" +
" 密码=" + pwd);
}
}
TestAccount.java
package com.bbedu.encap;
public class TestAccount {
public static void main(String[] args) {
// 创建Account
Account account = new Account();
account.setName("Jack Chan");
account.setBalance(6);
account.setPwd("222");
account.showInfo();
Account tim = new Account("Tim", 123, "233233");
tim.showInfo();
}
}
*继承
基本介绍
继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个嘞存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends
来声明继承父类即可
基本语法
class 子类 extends 父类{
}
- 子类会自动拥有父类定义的属性和方法
- 父类又叫超类,基类
- 子类又叫派生类
快速入门
package com.bbedu.extend_;
// 父类,是 Pupil 和 Graduate 的父类
public class Student {
// 共有属性
public String name;
public int age;
private double score;
// 共有方法
public void setScore(double score) {
this.score = score;
}
public void showInfo() {
System.out.println("学生名 " + name +
" 年龄 " + age +
" 成绩 " + score);
}
}
package com.bbedu.extend_;
public class Pupil extends Student{
public void testing() {
System.out.println("小学生" + name + "正在考小学数学..");
}
}
package com.bbedu.extend_;
public class Graduate extends Student{
public void testing() {
System.out.println("大学生 " + name + "正在考高等数学..");
}
}
package com.bbedu.extend_;
public class Extends01 {
public static void main(String[] args) {
Pupil pupil = new Pupil();
pupil.name = "Lily";
pupil.age = 10;
pupil.testing();
pupil.setScore(70);
pupil.showInfo();
System.out.println("==========");
Graduate graduate = new Graduate();
graduate.name = "Kris";
graduate.age = 20;
graduate.testing();
graduate.setScore(60);
graduate.showInfo();
}
}
细节
- 子类继承所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性不能直接在子类访问,要通过公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器;如果父类没有无参构造器,则必须在子类的构造器中用
super
去指定使用父类的哪个构造器去完成对父类的初始化工作,否则编译不会通过 - 如果希望指定去调用父类的某个构造器,则需要显示地调用:super(参数列表)
- super 必须放在方法的第一行(super 只能在构造器中使用)
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类,Object 是所有类的基类
- 父类构造器的调用不限于直接父类,将一直往上追溯直到 Object 类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制
- 不能滥用继承,子类和父类之间必须要满足 is-a 的逻辑关系
继承的本质
package com.bbedu.extend_;
/**
* 继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son(); // 内存的布局
// 要按查找规则返回信息
// 首先看子类是否有该属性,如果有且能访问,则返回
// 如果子类没有这个信息,就看父类有没有这个属性,如果有且能访问,则访问
// 依次向上找
System.out.println(son.name);
System.out.println(son.getFatherAge());
System.out.println(son.hobby);
}
}
class GrandPa {
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa {
String name = "大头爸爸";
private int age = 39;
public int getFatherAge() {
return age;
}
}
class Son extends Father {
String name = "大头儿子";
}
练习
会输出:
a
b name
b
正确
会输出:
我是A类
hahah我是B类的有参构造
我是c类的有参构造
我是c类的无参构造
package com.bbedu.extend_.exercise;
class PC extends Computer {
private String brand;
public PC(String cpu, String memory, String drive, String brand) {
super(cpu, memory, drive);
this.brand = brand;
}
public void printInfo(){
System.out.println(getDetails() + " brand=" + brand);
}
}
package com.bbedu.extend_.exercise;
class Computer {
private String cpu;
private String memory;
private String drive;
public Computer(String cpu, String memory, String drive) {
this.cpu = cpu;
this.memory = memory;
this.drive = drive;
}
public String getDetails(){
return ("CPU型号:" + cpu +
" 内存大小:" + memory +
" 硬盘大小:" + drive);
}
}
package com.bbedu.extend_.exercise;
class NotePad extends Computer {
private String color;
public NotePad(String cpu, String memory, String drive, String color) {
super(cpu, memory, drive);
this.color = color;
}
public void printInfo(){
System.out.println(getDetails() + " color=" + color);
}
}
package com.bbedu.extend_.exercise;
public class ExtendsExercise03 {
public static void main(String[] args) {
PC pc = new PC("Apple", "16GB", "512GB", "Apple");
pc.printInfo();
NotePad notePad = new NotePad("Qualcomm", "6GB", "128GB", "Silver");
notePad.printInfo();
}
}
super 关键字
基本介绍
super 代表父类的引用,用于访问父类的属性、方法、构造器
基本语法
- 访问父类的属性,但不能访问父类的 private 属性
- 访问父类的方法,不能访问父类的 private 方法
- 访问父类的构造器:super(参数列表),只能放在构造器的第一句,且只能出现一句
好处、细节
- 调用父类的构造器的好处:分工明确,父类属性由父类初始化,子类的属性由子类初始化
- 当子类中有和父类中的成员重名时,为了访问父类中的成员,则必须使用 super,若不重名,使用 super、 this 和 直接访问 效果相同
- super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员,如果多个基类中都有相同名字的成员,使用 super 时遵循就近原则。A -> B -> C
package com.bbedu.super_;
public class Base {
public int n1 = 999;
public int age = 111;
public void cal(){
System.out.println("Base类的 cal() 方法");
}
public void eat() {
System.out.println("Base类的 eat() 方法");
}
}
package com.bbedu.super_;
public class A extends Base {
// 四个属性
// public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public A(){
}
public A(String name){
}
public A(String name, int age){
}
// public void cal(){
// System.out.println("A类的 cal() 方法");
// }
public void test100(){
}
protected void test200(){
}
void test300(){
}
private void test400() {
}
}
package com.bbedu.super_;
public class B extends A {
public int n1 = 888;
// 访问父类的构造器:super(参数列表),只能放在构造器的第一句,且只能出现一句
public B() {
// super();
// super("Jack");
super("Tim", 20);
}
public void cal() {
System.out.println("B类的 cal() 方法");
}
public void sum() {
System.out.println("B类的 sum() 方法");
// 调用父类A的cal方法,有三种方法:
// cal(): 先找本类,再向父类追溯
// 若找到但不能访问,则报错,若找不到,则提示不存在
cal();
// 等价于 cal()
this.cal();
// 没有查找本类的过程,直接查找父类
super.cal();
// 访问属性的规则, n1 和 this.n1 相同
// 本类没有则查找父类
System.out.println(n1);
System.out.println(this.n1);
System.out.println(super.n1);
System.out.println(age);
eat();
}
// 访问父类的属性,但不能访问父类的 private 属性
public void hi() {
System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
}
// 访问父类的方法,不能访问 private 方法
public void ok() {
// super.test100();
// super.test200();
super.test300();
}
}
package com.bbedu.super_;
public class super01 {
public static void main(String[] args) {
B b = new B();
b.sum();
}
}
super 和 this 的比较
No. | 区别点 | this | super |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有则从父类继续查找 | 从父类开始查找属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有,则从父类继续查找 | 从父类开始查找方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类的构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
方法重写 / 覆盖(override)
基本介绍
方法重写就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
细节
- 子类的方法的参数,方法名称,要和父类方法一致
- 子类方法的返回类型和父类返回方法一致,或是父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限
练习
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载(overload) | 本类 | 必须一样 | 类型,个数或顺新至少有一个不同 | 无要求 | 无要求 |
重写(override) | 父子类 | 必须一样 | 相同 | 一致或子是父的子类 | 子类不能缩小 |
package com.bbedu.override_;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say(){
String s = "name = " + this.name + " age = " + this.age;
return s;
}
}
package com.bbedu.override_;
public class Student extends Person{
private String id;
private int score;
public Student(String name, int age, String id, int score) {
super(name, age);
this.id = id;
this.score = score;
}
@Override
public String say() {
return super.say() + " id = " + id +
" score = " + score;
}
}
package com.bbedu.override_;
public class OverrideExercise {
public static void main(String[] args) {
Person jack = new Person("Jack", 35);
System.out.println(jack.say());
Student student = new Student("Tim", 20, "123456", 100);
System.out.println(student.say());
}
}
*多态(polymorphic)
基本介绍
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的
- 重写和重载就体现多态
对象的多态(核心)
(1) 一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时,就确定了,不能改变
(3) 运行类型是可以变化的
(4) 编译类型看定义时 = 的左边,运行类型看 = 的右边
package com.bbedu.poly_.objectpoly_;
public class Animal {
public void cry(){
System.out.println("Animal 动物在叫...");
}
}
package com.bbedu.poly_.objectpoly_;
public class Dog extends Animal {
public void cry(){
System.out.println("Dog 小狗在叫...");
}
}
package com.bbedu.poly_.objectpoly_;
public class Cat extends Animal {
public void cry(){
System.out.println("Cat 小猫在叫...");
}
}
package com.bbedu.poly_.objectpoly_;
public class PolyObject {
public static void main(String[] args) {
Animal animal = new Dog();
animal.cry();
animal = new Cat();
animal.cry();
}
}
细节
*多态的前提是:两个对象(类)存在继承关系
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
特点:
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员
- 无法调用子类的特有成员,因为在编译阶段,能调用那些成员,是由编译类型来绝决定的
- 最终的运行效果看子类的具体实现
package com.bbedu.poly_.detail_;
public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello 你好");
}
}
package com.bbedu.poly_.detail_;
public class Cat extends Animal{
public void eat(){
System.out.println("Cat 猫吃鱼");
}
public void catchMouse(){
System.out.println("Cat 猫猫抓老鼠");
}
}
package com.bbedu.poly_.detail_;
public class PolyDetail {
public static void main(String[] args) {
// 向上转型,父类的引用指向了子列的对象
Animal animal = new Cat();
Object object = new Cat(); // 可行
// (1)可以调用父类中的所有成员
// animal.catchMouse(); // 错误,(2)无法调用子类的特有成员
// (3)因为在编译阶段,能调用那些成员,是由编译类型来绝决定的
// (4)最终的运行效果看子类的具体实现
// 调用方法时,按照从子类开始查找方法,规则与前面的方法调用一致
animal.eat();
animal.run();
animal.show();
animal.sleep();
}
}
多态的向下转型
- 语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中的所有成员
细节
属性没有重写之说,属性的值看编译类型
package com.bbedu.poly_.detail_; public class PolyDetail02 { public static void main(String[] args) { Sub sub = new Sub(); System.out.println(sub.count); Base base = new Sub(); System.out.println(base.count); } } class Base { int count = 10; } class Sub extends Base { int count = 20; }
instanceof 比较操作符,用于判断对象 的运行类型是否为XX类型或XX类型的子类型
package com.bbedu.poly_.detail_; public class PolyDetail03 { 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, 运行类型 BB AA aa = new BB(); System.out.println(aa instanceof AA); // true System.out.println(aa instanceof BB); // true Object obj = new Object(); System.out.println(obj instanceof AA); // false String str = "hello"; System.out.println(str instanceof Object); // true } } class AA { } class BB extends AA { }
练习
package com.bbedu.poly_;
public class PolyExercise02 {
public static void main(String[] args) {
Sub sub = new Sub();
System.out.println(sub.count);
sub.display();
Base b = sub;
System.out.println(sub == b);
System.out.println(b.count);
b.display();
}
}
class Base {
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
属性看编译,方法看运行
java 的动态绑定机制 (重要)
- 当调用对象方法时,该方法会和该对象的内存地址/运行类型 绑定
- 当调用属性时,没有动态绑定机制,哪里声明,哪里使用
package com.bbedu.poly_.danamic_;
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
// 子类中有的话,为40
// 子类没有的话,getI()调用子类的,此为动态绑定,20+10,为30
System.out.println(a.sum());
// 属性没有动态绑定,10+10,为20
System.out.println(a.sum1());
}
}
class A{
public int i = 10;
public int sum(){
return getI() + 10;
}
public int getI() {
return i;
}
public int sum1() {
return i + 10;
}
}
class B extends A {
public int i = 20;
// public int sum() {
// return i + 20;
// }
@Override
public int getI() {
return i;
}
// public int sum1() {
// return i + 10;
// }
}
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
package com.bbedu.poly_.polyarr_;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
return "name = " + name + "\tage = " + age;
}
}
package com.bbedu.poly_.polyarr_;
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say() + "\tscore=" + score;
}
public void study() {
System.out.println("学生 " + getName() + " 正在学习");
}
}
package com.bbedu.poly_.polyarr_;
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say() + "\tsalary=" + salary;
}
public void teach() {
System.out.println("老师 " + getName() + " 正在上课");
}
}
package com.bbedu.poly_.polyarr_;
public class PolyArray {
public static void main(String[] args) {
// 1 Person、2 Student、1 Teacher
Person[] persons = new Person[5];
persons[0] = new Person("Tim", 20);
persons[1] = new Student("Sam", 20, 80);
persons[2] = new Student("Amy", 30, 60);
persons[3] = new Teacher("Rain", 50, 20000);
persons[4] = new Teacher("Peter", 34, 14000);
for (int i = 0; i < persons.length; i++) {
// person[i] 的编译类型都为 Person,运行类型根据实际情况
System.out.println(persons[i].say());
// 判断运行类型,向下转型
if(persons[i] instanceof Student){
// Student s = (Student) persons[i];
// s.study();
((Student) persons[i]).study();
}
else if(persons[i] instanceof Teacher){
// Teacher t = (Teacher) persons[i];
// t.teach();
((Teacher) persons[i]).teach();
}
else {
System.out.println("不是学生也不是老师");
}
}
}
}
多态参数
package com.bbedu.poly_.polyparameter_;
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public double getAnnual() {
return 12 * 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;
}
}
package com.bbedu.poly_.polyparameter_;
public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work(){
System.out.println("员工 " + getName() + " is working");
}
@Override
public double getAnnual() {
// 直接调用父类方法
return super.getAnnual();
}
}
package com.bbedu.poly_.polyparameter_;
public class Manager extends Employee {
private double bonus;
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(){
System.out.println("经理 " + getName() + " is managing");
}
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
package com.bbedu.poly_.polyparameter_;
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("Tom", 7000);
Manager sam = new Manager("Sam", 10000, 30000);
PolyParameter polyParameter = new PolyParameter();
polyParameter.showEmpAnnual(tom);
polyParameter.showEmpAnnual(sam);
polyParameter.testWork(tom);
polyParameter.testWork(sam);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
public void testWork(Employee e){
// 向下转型
if(e instanceof Worker) {
((Worker) e).work();
}else if(e instanceof Manager){
((Manager) e).manage();
}else {
System.out.println("不做处理...");
}
}
}
Object 类
equals 方法
== 和 equals 的对比
== 是一个比较运算符
- 既可以判断基本类型,又可以判断引用类型
- 如果判断基本类型,判断的是 值 是否相等
- 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
euqals 方法
- euqals 是Object 类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如 Integer, String
练习
package com.bbedu.object_;
public class EuqalsExercise01 {
public static void main(String[] args) {
Person person = new Person("Tom", 10, '男');
Person person1 = new Person("Tom", 10, '男');
Person person2 = new Person("Jack", 20, '男');
System.out.println(person.equals(person1)); // 默认为false, Object 比较的是地址
System.out.println(person.equals(person2));
}
}
class Person {
private String name;
private int age;
private char gender;
@Override
public boolean equals(Object obj) {
// 如果比较的对象是同一个,则直接返回true
if (this == obj){ // this 即为调用此方法的当前对象
return true;
}
if(obj instanceof Person){ //类型是Person才比较
Person p = (Person) obj;
return this.name.equals(this.name) && this.age == p.age
&& this.gender == p.gender;
// if (((Person) obj).age == this.age &&
// ((Person) obj).name.equals(this.name) &&
// ((Person) obj).gender == this.gender){
// return true;
// }
}
return false;
}
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
false/true/false/true/false
true/true/true/false/true/编译错误(不是同一种对象)
字符的本质是整数
hashCode
返回该对象的哈希码值
- 提高哈结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要是根据地址来计算的,但不能完全等价为地址
- 示例
- 后面的集合中再重写该方法
package com.bbedu.object_;
public class HashCode_ {
public static void main(String[] args) {
AA aa = new AA();
AA aa2 = new AA();
AA aa3 = aa;
System.out.println("aa.hashCode()=" + aa.hashCode());
System.out.println("aa2.hashCode()=" + aa2.hashCode());
System.out.println("aa3.hashCode()=" + aa3.hashCode());
}
}
class AA
toString
- 返回该对象的字符串表示
- 默认返回:全类型+@+哈希值的十六进制
- 重写 toString 方法,打印对象或拼接对象式,都会自动调用该对象的 toString 形式
- 当直接输出一个对象时,toString 方法就会默认调用该对象的 toString 方法
package com.bbedu.object_;
public class ToString_ {
public static void main(String[] args) {
/*
jdk 源码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
Monster monster = new Monster("妖怪", "巡山", 2000);
System.out.println(monster.toString());
System.out.println(monster.hashCode() + " HEX: " + Integer.toHexString(monster.hashCode()));
// 直接输出一个对象时,toString 方法就会默认调用该对象的 toString 方法
System.out.println(monster);
}
}
class Monster {
private String name;
private String job;
private double sal;
// 重写输出对象的属性
@Override
public String toString() { // 一般是输出对象的属性
return "Monster{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
'}';
}
public Monster(String name, String job, double sal) {
this.name = name;
this.job = job;
this.sal = sal;
}
}
finalize (废弃)
当对象被回收时,系统自动会调用该对象的 finalize 方法,子类可以重写该方法,做一些释放资源的操作
什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会调用 finalize() 方法
断点调试 (debug)
需求
断点调试可以一步一步的看源码执行的过程,从而发现错误所在
在断点调试过程中,是运行状态,是以对象的运行类型来执行的
介绍
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug
- 断点调试是程序员必须掌握的技能。
- 断点调试也能帮助我们查看java底层源代码的执行过程;提高程序员的Java水平。
快捷键
F7 跳入 F8 跳过、逐行执行 shift + F8 跳出 F9 resume, 执行到下一个断点
案例
1.
package com.bbedu.debug_;
public class Debug01 {
public static void main(String[] args) {
// 演示逐行执行
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += i;
System.out.println("i=" + i);
System.out.println("sum=" + sum);
}
System.out.println("退出for...");
}
}
2.
package com.bbedu.debug_;
public class Debug02 {
public static void main(String[] args) {
int[] arr = {1, 10, -1};
for (int i = 0; i <= arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("退出for...");
}
}
- 强制进入,查看源码
package com.bbedu.debug_;
import java.util.Arrays;
public class Debug03 {
public static void main(String[] args) {
int[] arr = {8, -1, 10, 98, 45};
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}
- resume, 动态下断点
package com.bbedu.debug_;
import java.util.Arrays;
public class Debug04 {
public static void main(String[] args) {
int[] arr = {8, -1, 10, 98, 45};
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
System.out.println("Hello100");
System.out.println("Hello200");
System.out.println("Hello300");
System.out.println("Hello400");
System.out.println("Hello500");
System.out.println("Hello600");
}
}
jdk 源码也可下断点调试,不能到的会有下图显示:
练习
项目实战-零钱通
化繁为简
- 先完成显示菜单,并且可以选择,给出对应的提示信息
- 完成零钱通明细
- 完成收益入账
- 消费
- 退出,确认退出
- 判断入账和消费金额是否合理
- 将面向过程的代码改为面向对象的
面向过程版本:
package com.bbedu.smallchange;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import static java.lang.Thread.sleep;
public class SmallChangeSys {
public static void main(String[] args) throws InterruptedException {
// 定义相关变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String key = "";
String details = "\n------------------零钱通明细------------------";
double money = 0;
double balance = 0;
Date date = null; // Date 是 java.util.Date 类型,表示日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String note = null;
String confirm = null;
do {
System.out.println("\n------------------零钱通菜单------------------");
System.out.println("\t\t\t1 零钱通明细");
System.out.println("\t\t\t2 收 益 入 账");
System.out.println("\t\t\t3 消 费");
System.out.println("\t\t\t4 退 出");
System.out.print("请选择(1-4):");
key = scanner.next();
switch (key){
case "1":
System.out.println(details);
break;
case "2":
System.out.print("收益入账金额:");
money = scanner.nextDouble();
// money 应该校验
// 不合理的直接退出
if (money <= 0) {
System.out.println("! 收益入账金额需要大于0");
break;
}
balance += money;
date = new Date();
// 日期格式化
details += "\n收益入账\t" + "+¥" + money
+ "\t" + sdf.format(date) + " 余额:" + balance;
break;
case "3":
System.out.println("消费金额:");
money = scanner.nextDouble();
// 校验
if ( money <= 0 || money > balance){
System.out.println("! 消费金额应在 (0-"+balance+")");
break;
}
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
date = new Date();
details += "\n" + note + "\t-¥" + money + "\t" +
sdf.format(date) + "\t余额:" + balance;
break;
case "4":
// 编程遵守原子性
while (true){
System.out.println("确定要退出吗? (y/n)");
confirm = scanner.next();
if(confirm.equals("y") || confirm.equals("n")){
break;
}else {
System.out.println("! 输入有误, 请重新输入");
}
}
if (confirm.equals("y")){
System.out.print(" 退 出 中");
for (int i = 0; i < 3; i++) {
Thread.sleep(300);
System.out.print(" .");
}
System.out.println();
loop = false;
}
break;
default:
System.out.println("! 选择有误,请重新选择");
}
}while (loop);
System.out.println("-----零钱通已退出-----");
}
}
面向对象版本:
package com.bbedu.smallchange.oop;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/**
* 该类完成零钱通的各个功能
* 使用OOP
* 将各个功能对应应该方法
*/
public class SmallChangeSysOOP {
// 定义相关变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String key = "";
String details = "\n------------------零钱通明细------------------";
double money = 0;
double balance = 0;
Date date = null; // Date 是 java.util.Date 类型,表示日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String note = null;
String confirm = null;
public void showTab(){
do {
System.out.println("\n------------------零钱通选择菜单------------------");
System.out.println("\t\t\t1 零钱通明细");
System.out.println("\t\t\t2 收 益 入 账");
System.out.println("\t\t\t3 消 费");
System.out.println("\t\t\t4 退 出");
System.out.print("请选择(1-4):");
key = scanner.next();
switch (key){
case "1":
detail();
break;
case "2":
income();
break;
case "3":
pay();
break;
case "4":
exit();
break;
default:
System.out.println("! 选择有误,请重新选择");
}
}while (loop);
}
public void detail(){
System.out.println(details);
}
public void income(){
System.out.print("收益入账金额:");
money = scanner.nextDouble();
// money 应该校验
// 不合理的直接退出
if (money <= 0) {
System.out.println("! 收益入账金额需要大于0");
return; // 退出方法,不在执行后面的代码
}
balance += money;
date = new Date();
// 日期格式化
details += "\n收益入账\t" + "+" + money
+ "\t" + sdf.format(date) + " 余额:" + balance;
}
public void pay(){
System.out.println("消费金额:");
money = scanner.nextDouble();
// 校验
if ( money <= 0 || money > balance){
System.out.println("! 消费金额应在 (0-"+balance+")");
return;
}
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
date = new Date();
details += "\n" + note + "\t-" + money + "\t" +
sdf.format(date) + "\t余额:" + balance;
}
public void exit() {
// 编程遵守原子性
while (true){
System.out.println("确定要退出吗? (y/n)");
confirm = scanner.next();
if(confirm.equals("y") || confirm.equals("n")){
break;
}else {
System.out.println("! 输入有误, 请重新输入");
}
}
if (confirm.equals("y")){
System.out.println(" 退 出 中");
loop = false;
}
System.out.println("-----零钱通已退出-----");
}
}
package com.bbedu.smallchange.oop;
public class SmallChangeSysApp {
public static void main(String[] args) {
SmallChangeSysOOP smallChangeSysOOP = new SmallChangeSysOOP();
smallChangeSysOOP.showTab();
}
}
本章练习
package com.bbedu.homework;
public class Homework01 {
public static void main(String[] args) {
// 对象数组,注意声明方式:
Person[] people = new Person[3];
people[0] = new Person("Jack", 30, "Coder");
people[1] = new Person("Tim", 20, "teacher");
people[2] = new Person("Mary", 40, "seller");
for (int i = 0; i < people.length; i++) {
System.out.println(people[i]);
}
// 冒泡排序
for (int i = 0; i < people.length - 1; i++) {
for (int j = 0; j < people.length - i -1; j++) {
if (people[j].getAge() < people[j+1].getAge()){
int tmp = people[j].getAge();
people[j].setAge(people[j+1].getAge());
people[j+1].setAge(tmp);
}
}
}
System.out.println("===排序后===");
for (int i = 0; i < people.length; i++) {
System.out.println(people[i]);
}
}
}
class Person{
private String name;
private int age;
private String job;
public Person(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
package com.bbedu.homework;
public class Homework03 {
public static void main(String[] args) {
Professor professor = new Professor("Cook", 50, "CEO", 1500000, 1.3);
professor.introduce();
}
}
class Teacher{
private String name;
private int age;
private String post;
private double salary;
private double grade;
public Teacher(String name, int age, String post, double salary, double grade) {
this.name = name;
this.age = age;
this.post = post;
this.salary = salary;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getGrade() {
return grade;
}
public void setGrade(double grade) {
this.grade = grade;
}
public void introduce(){
System.out.print("name=" + name + " age=" + age
+ " post=" + post + " salary=" + salary);
}
}
class Professor extends Teacher{
public Professor(String name, int age, String post, double salary, double grade) {
super(name, age, post, salary, grade);
}
@Override
public void introduce() {
super.introduce();
System.out.println(" grade=" + this.getGrade());
}
}
package com.bbedu.homework;
public class Homework04 {
public static void main(String[] args) {
Worker tim = new Worker("Tim", 100, 30);
tim.printSalary();
Manager cook = new Manager("Cook", 300, 20);
cook.printSalary();
}
}
class Employee {
private String name;
private double dayPay;
private int workDay;
public Employee(String name, double dayPay, int workDay) {
this.name = name;
this.dayPay = dayPay;
this.workDay = workDay;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getDayPay() {
return dayPay;
}
public void setDayPay(double dayPay) {
this.dayPay = dayPay;
}
public int getWorkDay() {
return workDay;
}
public void setWorkDay(int workDay) {
this.workDay = workDay;
}
public void printSalary(){
System.out.print("name=" + name + " dayPay=" + dayPay
+ "workDay="+ workDay);
}
}
class Worker extends Employee{
public Worker(String name, double dayPay, int workDay) {
super(name, dayPay, workDay);
}
@Override
public void printSalary() {
System.out.print("Worker ");
super.printSalary();
System.out.println(" salary=" + getWorkDay()*getDayPay()*1.0);
}
}
class Manager extends Employee{
public Manager(String name, double dayPay, int workDay) {
super(name, dayPay, workDay);
}
@Override
public void printSalary() {
System.out.print("Manager ");
super.printSalary();
System.out.println(" salary=" + getWorkDay()*getDayPay()*1.2);
}
}
package com.bbedu.homework.homework5;
public class Test {
public static void main(String[] args) {
Employee[] e = new Employee[5];
e[0] = new Worker("Sam", 100);
e[1] = new Waiter("Peter", 120);
e[2] = new Peasant("Tony", 80);
e[3] = new Teacher("Mary", 150, 20);
e[4] = new Scientist("Bob", 200, 100000);
for (int i = 0; i < e.length; i++) {
e[i].printSal();
}
}
}
package com.bbedu.homework.homework5;
public class Employee {
private String name;
private double sal;
public Employee(String name, double sal) {
this.name = name;
this.sal = sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public void printSal(){
System.out.println("name=" + name + " salary=" + sal*365);
}
}
package com.bbedu.homework.homework5;
public class Worker extends Employee {
public Worker(String name, double sal) {
super(name, sal);
}
@Override
public void printSal() {
System.out.print("Worker ");
super.printSal();
}
}
package com.bbedu.homework.homework5;
public class Peasant extends Employee {
public Peasant(String name, double sal) {
super(name, sal);
}
@Override
public void printSal() {
System.out.print("Peasant ");
super.printSal();
}
}
package com.bbedu.homework.homework5;
public class Teacher extends Employee {
private double dayPay;
public Teacher(String name, double sal, double dayPay) {
super(name, sal);
this.dayPay = dayPay;
}
public double getDayPay() {
return dayPay;
}
public void setDayPay(double dayPay) {
this.dayPay = dayPay;
}
@Override
public void printSal() {
System.out.print("Teacher ");
System.out.println("name=" + getName() + " salary=" + (getSal()+getDayPay())*365);
}
}
package com.bbedu.homework.homework5;
public class Scientist extends Employee{
private double bonus;
public Scientist(String name, double sal, double bonus) {
super(name, sal);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public void printSal() {
System.out.print("Scientist ");
System.out.println("name=" + getName() + " salary=" + (getSal()*365+getBonus()));
}
}
package com.bbedu.homework.homework5;
public class Waiter extends Employee{
public Waiter(String name, double sal) {
super(name, sal);
}
@Override
public void printSal() {
System.out.print("Waiter ");
super.printSal();
}
}
package com.bbedu.homework.homework8;
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
balance -= amount;
}
public double getBalance() {
return balance;
}
}
package com.bbedu.homework.homework8;
public class CheckingAccount extends BankAccount {
public CheckingAccount(double initialBalance) {
super(initialBalance);
}
@Override
public void deposit(double amount) {
super.deposit(amount - 1);
}
@Override
public void withdraw(double amount) {
super.withdraw(amount + 1);
}
}
package com.bbedu.homework.homework8;
public class SavingAccount extends BankAccount{
private int freeCount = 3;
private double rate = 0.01; // 利率
public SavingAccount(double initialBalance) {
super(initialBalance);
}
public int getFreeCount() {
return freeCount;
}
public void setFreeCount(int freeCount) {
this.freeCount = freeCount;
}
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
@Override
public void deposit(double amount) {
if (freeCount > 0){
super.deposit(amount);
} else {
super.deposit(amount - 1);
}
freeCount--;
}
@Override
public void withdraw(double amount) {
if (freeCount > 0){
super.withdraw(amount);
} else {
super.withdraw(amount + 1);
}
freeCount--;
}
public void earnMonthlyInterest(){
freeCount = 3;
super.deposit(getBalance()*rate);
}
}
package com.bbedu.homework.homework8;
public class Homework08 {
public static void main(String[] args) {
// CheckingAccount checkingAccount = new CheckingAccount(1000);
// checkingAccount.deposit(100);
// checkingAccount.withdraw(10);
// System.out.println(checkingAccount.getBalance());
SavingAccount savingAccount = new SavingAccount(1000);
savingAccount.deposit(100);
savingAccount.deposit(100);
savingAccount.deposit(100);
System.out.println(savingAccount.getBalance());
savingAccount.deposit(100);
System.out.println(savingAccount.getBalance());
savingAccount.earnMonthlyInterest();
savingAccount.deposit(100);
System.out.println(savingAccount.getBalance());
}
}
public LabeledPoint(String label, double x, double y) {
super(x, y);
this.label = label;
}
package com.bbedu.homework.homework10;
public class Doctor {
private String name;
private int age;
private String job;
private String gender;
private double sal;
public Doctor(String name, int age, String job, String gender, double sal) {
this.name = name;
this.age = age;
this.job = job;
this.gender = gender;
this.sal = sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(!(obj instanceof Doctor)){
return false;
}
Doctor doctor = (Doctor) obj;
return doctor.age == this.age && doctor.sal == this.sal
&& doctor.name.equals(this.name) && doctor.job.equals((this.job))
&& doctor.gender.equals(this.gender);
}
}
package com.bbedu.homework.homework11;
import java.rmi.StubNotFoundException;
public class Homework11 {
public static void main(String[] args) {
// 向上转型
Person p = new Student();
// 可以用 Person 的 eat()和 run() 方法, 但run方法被Student类重写
// 不能调用子类的特有方法, 因为编译类型为父类
p.eat();
p.run();
// 向下转型
Student s = (Student) p;
// 可以调用子类的所有方法
s.run();
s.eat();
s.study();
}
}
评论