P273~P359
包
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
实际上就是创建不同的文件夹来保存类文件
语法
package 包名;
包的命名规则
只能包含数字、字母、下划线、小圆点,不能数字开头,不能关键字或保留字
命名规范
一般是 com.公司名.项目名.业务模块名
常用的包
- java.lang.*,基本包,默认引入,不需要再引入
- java.util.*,util包,系统提供的工具包,工具类,使用Scanner
- java.net.*,网络包,网络开发
- java.awt.*,做java的界面开发,GUI
新建包
在src文件夹 右键→软件包 输入 com.xiaoming
如何引入包,Import01.java/
package com.pkg;
import java.util.Arrays;
import java.util.Scanner;
public class Import01 {
public static void main(String[] args) {
//使用系统提供的Arrays 完成数组排序
int[] arr = {-1,20,2,13,3};
//对其排序
//传统方法是,自己编写排序
//系统提供相应的类,可以方便完成 Arrays
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
注意事项和细节,PkgDetail.java
package的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package
import
指令,位置放在package
的下面,在类定义前面,可以有多句且没有顺序要求。
package com.pkg;
import java.util.Arrays;
import java.util.Scanner;
public class PkgDetail {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int[] arr = {-1, 2, 2};
Arrays.sort(arr);
}
}
访问修饰符
用于控制方法和属性的访问权限
- 公开:public,对外公开
- 受保护:protected,对子类和同一个包中的类公开
- 默认:没有修饰符号,向同一个包的类公开
- 私有:private,只有类本身可以访问
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包<br> |
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | x |
默认 | 无 | √ | √ | x | x |
私有 | private | √ | x | x | x |
访问修饰符细节
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
- 因为没有学习继承,因此关于子类中的访问权限,讲完字类后再看
- 成员方法的访问规则和属性完全一样
封装
把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作,才能对数据进行操作
好处
- 隐藏实现细节
- 可以对数据进行验证,保证安全合理
实现步骤
- 将属性进行私有化private
- 提供一个公共的set方法,用于对属性判断并赋值
- 提供一个公共的get方法,用于对获取属性的值
快速入门,com.encap.Encapsulation01.java
不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证,年龄合理就设置,否则就默认
年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间
package com.encap;
public class Encapsulation01 {
public static void main(String[] args) {
Person person = new Person();
person.name = "Jack";
person.setAge(10);
person.setSalay(100.5);
System.out.println(person.info());
Person person1 = new Person("smith", 200, 2000);
System.out.println(person1.info());
}
}
// 不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证,
// 年龄合理就设置,否则就默认
// 年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间
class Person {
public String name;
private int age;//年龄私有化
private double salay;
//构造器
public Person() {
}
//三个属性的构造器
//可以将set方法写在构造器中,这样仍然可以验证数据
public Person(String name, int age, double salay) {
setName(name);
setAge(age);
setSalay(salay);
}
//set,get方法生成快捷键
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
} else {
System.out.println("名字长度不对");
this.name = "无名";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
// 年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间
if (age >= 1 && age <= 120) {
this.age = age;
} else {
System.out.println("设置年龄错误,年龄需要在1-120");
this.age = 18;
}
}
public double getSalay() {
return salay;
}
public void setSalay(double salay) {
this.salay = salay;
}
public String info() {
return "信息为" + name + " " + getAge() + " " + getSalay();
}
}
课堂练习,com.encap.AccountTest.java和Account.java
创建程序,在其中定义两个类:Account和AccountTest类,体会java封装性
- Account类要求具有属性:姓名(长度为2,3,4)、余额(>20),密码(6位),如果不满足给出提示信息,并给默认值
- 通过setXxx方法给Account的属性赋值
- 在AccountTest中测试
package com.encap;
public class Account {
private String name;
private int account;
private String code;
public Account(String name, int account, String code) {
setAccount(account);
setCode(code);
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 4) {
this.name = name;
} else {
System.out.println("name长度不对");
this.name = "无名";
}
}
public int getAccount() {
return account;
}
public void setAccount(int account) {
if (account > 20) {
this.account = account;
} else {
System.out.println("account<20");
this.account = 20;
}
}
public String getCode() {
return code;
}
public void setCode(String code) {
if (code.length() == 6) {
this.code = code;
} else {
System.out.println("密码长度不为6");
this.code = "111111";
}
}
public String info() {
return "信息为" + getName() + " " + getAccount();
}
}
import com.encap.Account;
public class AccountTest {
public static void main(String[] args) {
Account ac = new Account("aq",30,"abcdef");
System.out.println(ac.info());
}
}
继承
继承可以解决代码复用,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的字类不需要重新定义这些属性和方法,只需要通过extends
来声明继承父类即可。
继承基本语法
class 子类 extends 父类{
}
- 子类会自动拥有父类定义的属性和方法
- 父类又叫超类、基类
- 子类又叫派生类
细节
- 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定父类的哪个构造器完成对父类的初始化工作。
- 如果希望指定去调用父类的某个构造器,则显式的调用一下
- super在使用时,需要放在构造器的第一行
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类
- 父类构造器的调用不限于直接父类,将一直追溯到Object类
- 子类最多只能继承一个父类,即java 中是单继承机制
- 不能滥用继承,子类和父类必须满足 is a 的逻辑 Cat is an Animal Cat extends Animal
返回细节
按照查找关系来返回信息
- 首先看子类是否有该属性
- 如果子类有这个属性,并且可以访问,则返回信息
- 如果子类没有这个属性,就看父类有没有这个属性
- 如果父类没有,就按照(3)的规则,继续找上级父类,直到Object
练习,com.extend.ExtendsExercise.java
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌brand】
编写NotePad子类,继承Computer类,添加特有属性【演示color】
编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息
package com.extend;
public class ExtendsExercise {
public static void main(String[] args) {
PC pc = new PC("i7", "1G", "100G", "huawei");
NotePad np = new NotePad("i7", "2G", "200G", "green");
System.out.println(pc.getDetails());
System.out.println(np.getDetails());
}
}
class Computer {
private String CPU;
private String cache;
private String disk;
public Computer(String CPU, String cache, String disk) {
this.CPU = CPU;
this.cache = cache;
this.disk = disk;
}
public String getDetails() {
return "信息为:CPU: " + CPU + " cache: " + cache + " disk: " + disk;
}
}
class PC extends Computer {
private String brand;
public PC(String CPU, String cache, String disk, String brand) {
super(CPU, cache, disk);
this.brand = brand;
}
public String getDetails() {
return super.getDetails() + " brand: " + brand;
}
}
class NotePad extends Computer {
private String color;
public NotePad(String CPU, String cache, String disk, String color) {
super(CPU, cache, disk);
this.color = color;
}
}
super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法
//1.访问父类的属性,但不能访问父类private属性
super.属性名;
//2.访问父类的方法,但不能访问父类private方法
super.方法名(参数列表);
//3.访问父类的构造器
super(参数列表);//只能放在构造器第一句
使用细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)
- 当子类中有和父类中的成员(属性、方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则
super和this的比较
no | 区别点 | this | super<br> |
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 | 访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法,则从父类中继续查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类的构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
方法重写/覆盖(override)
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数都一样,那么我们就说子类的这个方法覆盖了父类的方法
方法重写细节
方法重写要满足以下条件:
- 子类方法的参数方法名要和父类参数方法名完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限
方法重写和重载的比较
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符<br> |
重载 | 本类 | 必须一样 | 类型,个数或者顺序至少一个不同 | 无要求 | 无要求 |
重写 | 父子类 | 必须一样 | 必须相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
方法重写练习,com.override.OverrideExercise.java
- 编写一个Person类,包括属性(private name, age),构造器、方法say(返回自我介绍)
- 编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法
- 在main中,分别创建Person和Student对象,调用say方法输出自我介绍
package com.override;
public class OverrideExercise {
public static void main(String[] args) {
Person person = new Person("a", 11);
Student student = new Student("a", 12, 1, 100);
System.out.println(person.say());
System.out.println(student.say());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say() {
return "姓名:" + name + " 年龄:" + age;
}
}
class Student extends Person {
private int id;
private int score;
public Student(String name, int age, int id, int score) {
super(name, age);
this.id = id;
this.score = score;
}
public String say() {
return super.say() + " id:" + id + " 分数:" + score;
}
}
多态
方法或对象具有多种形态。
- 重写和重载体现多态
- 对象多态,com.poly.PolyObject.java
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 左边,运行类型看 = 右边 Animal animal = new Dog() animal编译类型时Animal,运行类型是Dog animal = new CatI() animal运行类型变成Cat,编译类型仍是Animal
package com.poly;
public class PolyObject {
public static void main(String[] args) {
Animal animal = new Dog();
animal.cry();
animal = new Cat();
animal.cry();
}
}
class Animal {
public void cry() {
System.out.println("Animal cry()...");
}
}
class Cat extends Animal {
@Override
public void cry() {
System.out.println("Cat cry()...");
}
}
class Dog extends Animal {
@Override
public void cry() {
System.out.println("Dog cry()...");
}
}
多态细节
-
向上转型
-
本质:父类的引用指向了子类的对象
- Animal animal = new Dog();
-
语法:父类类型 引用名 = new 子类类型();
-
特点:
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员
- 不能调用子类中特有成员
- 最终运行效果看子类的具体实现
-
-
向下转型
-
语法:子类类型 引用名 = (子类类型)父类引用;
- Cat cat = (Cat) animal;
-
只能强转父类的引用,不能强转父类的对象
-
要求父类的引用必须指向的是当前目标类型的对象
-
向下转型后可以调用子类类型中所有的成员
-
-
属性没有重写之说
-
instanceOf,用于判断对象的运行类型是否为XX类型或XX类型的子类型
java动态绑定机制
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
当调用对象属性的时候,没有动态绑定机制,哪里声明,哪里使用
多态应用
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
Object类详解
equals方法
== 和equals的对比
- ==:既可以判断基本类型,又可以判断引用类型
- ==:如果判断基本类型,判断的是值是否相等
- ==:如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
- equals:是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String
hashCode方法
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向同一个对象,则哈希值肯定一样
- 两个引用,如果指向不同对象,则哈希值不一样
- 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
- 在集合中,hashCode也会重写
toString方法
返回:全类名+@+哈希值的十六进制。
子类往往重写toString方法,用于返回对象的属性信息
finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收?当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动出发垃圾回收机制
项目 零钱通
项目需求说明
使用Java开发零钱通项目,可以完成收益入账,消费,查看明细,退出系统等功能
项目代码实现
SmallChangeSys.java完成基本功能,完成基本功能
代码实现改进
- 用户输入4退出时,给出提示“你确定要退出吗”y/n,必须输入正确的y/n,否则循环输入指令,直到输入y或n
- 在收益入账和消费时,判断金额是否合理
- 将面向过程的代码修改成面向对象的方法,编写SmallChangeSysOOP.java并使用SmallChangeSysApp.java完成测试
本章作业
1、定义一个Person类{name,age,job},初始化Person对象数组,有3个person对象,并按照age从大到小进行排序,提示,使用冒泡排序。Homework01.java
2、编写老师类,Homework02.java
- 要求有属性name、age、possalary
- 编写业务方法,introduce(),实现输出一个教师的信息
- 编写教师类的三个字类:教授类、副教授类、讲师类。工资级别:教授:1.3,副教授:1.2,讲师:1.1.在三个子类里重写introd()方法
- 定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印
package com.homework;
public class Homework01 {
public static void main(String[] args) {
Person[] pArr = new Person[3];
pArr[0] = new Person("a", 30, "driver");
pArr[1] = new Person("b", 20, "student");
pArr[2] = new Person("c", 40, "teacher");
Person temp;
for (int i = 0; i < pArr.length; i++) {
for (int j = i; j < pArr.length; j++) {
if (pArr[i].getAge() > pArr[j].getAge()) {
temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
}
}
for (int i = 0; i < pArr.length; i++) {
System.out.println(pArr[i].toString());
}
}
}
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 + '\'' +
'}';
}
}