10-Java面向对象中级
包
- 区分相同名字的类
- 当类很多时,包可以很好的管理类
- 控制访问范围
包的基本语法
package com.hspedu;
包的本质就是通过创建不同的文件夹来创建不同的类文件
- 快速入门
package com.use;
//import com.xiaoqiang.Dog;
public class Test {
public static void main(String[] args) {
com.xiaoqiang.Dog dog = new com.xiaoqiang.Dog();
System.out.println(dog);
com.xiaoming.Dog dog1 = new com.xiaoming.Dog();
System.out.println(dog1);
}
}
- 包的命名规则
- 声明包放在最上面
package com.hapedu;
//import java.util.Scanner;
//import java.util.*;
import java.util.Arrays;
public class Import01 {
public static void main(String[] args) {
// 使用数组类完成数组排序
int[] arr = {-1, 2, 30, 13, 3};
// 传统方法,自己编写排序,比如冒泡
// 系统提供相关的类,可以方便的完成Arrays
Arrays.sort(arr);
// 输出排序结果
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}
访问修饰符
package com.hspedu.modifier;
public class A {
// 四个属性,有四类访问修饰符,分别使用不同的访问修饰符修饰
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public void m1(){
// 该方法可以访问4个属性
System.out.println("n1=" + n1 + " n2 = " + n2 + " n3=" + n3 + " n4=" + n4);
}
protected void m2(){
}
void m3(){
}
private void m4(){
}
}
封装
- 封装就是把抽象出来的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过授权的【方法】,才能对数据进行操作
- 对电视机的操作就是典型的封装
- 封装带来的好处
1.可以隐藏实现细节和方法(连接数据库)<-调用(传入参数.....)
2.可以对数据进行验证,保证安全合理性 - 封装的实现步骤
- 封装的快速入门
package com.hspedu.encap;
public class Encapsu01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("jackagesf");
person.setAge(3000);
person.setSalary(1200);
System.out.println(person.info());
}
}
/*
*/
class Person{
public String name;
private int age;
private double salary;
// 自己写太慢,可以使用快捷键 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;
}
}
package com.hspedu.encap;
public class Encapsu01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("jackagesf");
person.setAge(3000);
person.setSalary(1200);
System.out.println(person.info());
// 如果我们使用构造器
Person smith = new Person("smith", 200, 15000);
System.out.println(smith.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;
setName(name);
setAge(age);
setSalary(salary);
}
// 自己写太慢,可以使用快捷键 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对姓名,余额,密码进行限定,并且要求不能在外面进行修改
package com.hspedu.encap;
public class Account {
private String name;
private double remainder;
private String password;
// 提供两个构造器
public Account(){
}
public Account(String name, double remainder, String password) {
setName(name);
setRemainder(remainder);
setPassword(password);
}
public void setName(String name) {
if(name.length() >=2 && name.length() <= 4){
this.name = name;
}
else {
System.out.println("name在4-6之间");
this.name = null;
}
}
public void setRemainder(double remainder) {
if(remainder > 20){
this.remainder = remainder;
}
else {
System.out.println("余额必须大于20,否则默认为0");
this.remainder = 0;
}
}
public void setPassword(String password) {
if(password.length() == 6){
this.password = password;
}
else{
System.out.println("密码长度为6位,否则默认为123456");
this.password = "123456";
}
}
public String getInfo(){
return "name=" + name + " remainder=" + remainder + " password=" + password;
}
}
继承
-
为什么需要继承?
-
快速入门
package com.hspedu.extend_.improve_;
// 父类,是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.hspedu.extend_.improve_;
public class Graduate extends Student{
public void testing(){
System.out.println("大学生" + name + "正在考大学数学.....");
}
}
-
继承的优势
-
代码的复用性提高了
-
代码的扩展性和维护性提高了
-
继承的细节
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
- 子类必须调用父类的构造器, 完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。)
- 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
- super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类, Object 是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即 java
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
- 继承的本质
如果父类的方法是私有的,但是爷爷的类是公有的,依然没有办法访问
package com.hspedu.extend_;
import java.util.StringTokenizer;
/**
* 继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son(); // 内存的本质
//(1 首先看子类是否有属性,
//(2 如果子类有这个属性,并且可以访问,则返回信息
//(3 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回相关信息...)
//(4 如果父类么有就按照(3)的规则,继续找到上级父类,直到object
System.out.println(son.name);
// System.out.println(son.age);
System.out.println(son.getAge());
System.out.println(son.hobby);
}
}
class GrandPa{
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa{
String name = "大头爸爸";
private int age = 39;
public int getAge() {
return age;
}
}
class Son extends Father{
String name = "大头儿子";
}
- 练习1 输出结果是
package com.hspedu.extend_.exercise;
public class ExtendsEx01 {
public static void main(String[] args) {
B b = new B();
}
}
class A {
A() {
System.out.println("a");
}
A(String name) {
System.out.println("a name");
}
}
class B extends A {
B() {
this("abc");
System.out.println("b");
}
B(String name) {
// super();
System.out.println("b name");
}
}
- 练习2 输出内容是多少?
package com.hspedu.extend_.exercise;
public class ExtendsEx03 {
public static void main(String[] args) {
PC pc = new PC("intel", 16, 500, "IBM");
pc.printInfo();
}
}
class Computer{
String cpu;
int memory;
int disk;
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
// 返回computer信息
public String getDetails(){
return "cpu=" + cpu + " memory=" + memory + " disk=" + disk;
}
}
class PC extends Computer{
String brand;
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public void printInfo(){
System.out.println("PC信息=");
// System.out.println(cpu);
System.out.println(getDetails() + " brand=" + brand);
}
}
class NotePad extends Computer{
String color;
public NotePad(String cpu, int memory, int disk, String color) {
super(cpu, memory, disk);
this.color = color;
}
public void getInfo(){
System.out.println(getDetails() + " NotePad Color=" + color);
}
}
-
super关键字
super 代表父类的引用,用于访问父类的属性、方法、构造器 -
super给编程带来的好处
1.super给编程带来的好处(分工明确,父类的属性由父类初始化,子类的属性由子类初始化)
2.当子类中和父类的成员(属性和方法)重名,为了访问父类的成员,必须通过super,如果没有重名,使用super,this,直接访问是一样的效果 -
方法的重写
子类的方法和父类名称,反馈类型,参数一样,重写 -
方法重写的细节
多态
方法或对象具有多种状态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的
-
多态的具体体现
-
方法的多态,就是之前讲的重载和重写
-
多态的具体体现
-
多态的注意事项和细节讨论
多态的前提是:两个类存在继承关系
多态向上转型
package com.hspedu.poly_.detail;
public class PolyDetail {
public static void main(String[] args) {
// 向上转型,父类的引用指向了子类的对象
Animal animal = new Cat();
//可以访问父类的所有成员(遵守访问权限
// 但是不能调用子类的特有成员
// 因为在编译阶段,能调用哪些成员是有编译类型决定的
// animal.catchMouse();
// 最终的运行效果还要看子类的实现,先找子类,然后找父类
animal.eat();
animal.run();
System.out.println("ok~~");
}
}
- 多态的向下转型
- 属性没有重写之说,属性直接看编译类型
package com.hspedu.poly_.detail;
public class PolyDetail02 {
public static void main(String[] args) {
// 属性没有重写之说,属性的值看编译类型
Base base = new Sub();
System.out.println(base.count); // ?看编译类型 10
Sub sub = new Sub();
System.out.println(sub.count);
}
}
class Base {
int count = 10;
}
class Sub extends Base{
int count = 20;
}
- instanceOf 是看运行类型是否是对象的类型或者子类
Java的动态绑定机制
多态的时候如果在父类中找方法的时候,存在父类和子类的方法,此时调用运行类型/内存地址的方法,即子类的方法
- 多态的运用
1.多态数组
package com.hspedu.poly_.polyarr_;
import com.use.Test;
public class PolyArr {
public static void main(String[] args) {
// 现在有一个继承结构如下,统一放入数组
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("micoo", 18, 100);
persons[2] = new Student("smith", 17, 30);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 250000);
// 循环遍历数组
for(int i = 0; i< persons.length; i++){
// 编译类型是Person,运行类型是实际情况
// System.out.println(persons[i].say());
// 判断persons的运行类型是否是Student
if(persons[i] instanceof Student){
Student student = (Student)persons[i]; // 向下转型
student.study();
// 也可以一条语句搞定,((Student)persons[i]).study()
}
else if(persons[i] instanceof Teacher){
((Teacher)persons[i]).teach();
}
else if(persons[i] instanceof Person){
continue;
}
else {
System.out.println("你的类型有误");
}
}
}
}
2.多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
package com.hspedu.poly_.polypara_;
import com.sun.corba.se.spi.orbutil.threadpool.Work;
public class PolyPara {
public static void main(String[] args) {
Worker tom = new Worker("tom", 1000);
Manager mick = new Manager("Mick", 2000, 600);
PolyPara polyPara = new PolyPara();
polyPara.showEmAnnual(tom);
polyPara.showEmAnnual(mick);
polyPara.testWork(tom);
polyPara.testWork(mick);
}
public void showEmAnnual(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 是Object类的方法,只能判断引用类型,但是子类中往往重写该方法,用于判断类型是否相等
HashCode
返回对象的哈希码值,支持此方法是为了提高哈希表的性能
toString
默认返回:全类名【包名+类名】+@+哈希值的16进制
finalize
断点调试
零钱通项目
package com.hspedu.smallchange;
import java.text.SimpleDateFormat;
import java.util.Scanner;
import java.util.Date;
public class SmallChangeSys {
// 化繁为简,先死后活
// 1.先完成显示菜单,给出选择菜单,给出相应提示
// 2.完成零钱通明细
// 3.收益入账
// 4.完成消费
public static void main(String[] args) {
// 定义相关的变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String key = "";
//2.完成零钱通明细
// 1,可以把收益入账和消费,保存到数组,2.可以使用对象,3.可以使用String拼接
String detail = "------------------零钱通明细------------------";
// 3.收益入账,入账了多少钱
// 思路:定义新的变量,完成功能驱动我们增加新的变量和代码
double money = 0;
double balance = 0;
Date date = null; // date是日期类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
// 4.消费增加变量
// 定义新的变量
String note = "";
// 退出
// 一段代码一个功能
// 改进
// 收益入账,
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.println("请选择(1-4):");
key = scanner.next();
// 使用switch分支结构
switch (key){
case "1":
System.out.println(detail);
break;
case "2":
System.out.println("收益入账金额:");
money = scanner.nextDouble();
// money 的值范围应该校验一下 一会在完善
// 找到不正确的金额,给出提示,直接break
if(money <= 0){
System.out.println("收益入账需大于0");
break;
}
balance += money;
// 拼接收益入账信息到details
date = new Date(); // 获取当前日期
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
detail += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + 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(); // 获取当前日期
detail += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
break;
case "4":
String choice = null;
while(true){
System.out.println("你确定要退出吗?y/n");
choice = scanner.next();
if("y".equals(choice) || "n".equals(choice)){
break;
}
}
if("y".equals(choice)){
loop = false;
}
break;
default:
System.out.println("选择有误,请重新选择");
}
} while (loop);
System.out.println("----退出了零钱通项目----");
}
}
- 练习01:对person类中的age进行排序
package com.hspedu.homework.homework01;
public class Homework01 {
public static void main(String[] args) {
// 初始化person数组
Person[] persons = new Person[3];
persons[0] = new Person("jack",10, "JavaEE工程师");
persons[1] = new Person("tom",50, "大数据工程师");
persons[2] = new Person("mary",30, "PHP工程师");
for(int i = 0; i < persons.length; i++){
System.out.println(persons[i]);
}
// 使用冒泡排序
Person temp = null;
for(int i = 0; i < persons.length-1; i++){
for(int j = 0; j < persons.length - i - 1; j++){
if(persons[j].getAge() > persons[j+1].getAge()){
temp = persons[j];
persons[j] = persons[j+1];
persons[j+1] = temp;
}
}
}
for(int i = 0; i < persons.length; i++){
System.out.println(persons[i]);
}
}
}
/*
定义一个Person类,{name,age, job} 初始化Person对象数组,有3个对象,
通过age进行从大到小排序,使用冒泡排序
*/
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 + '\'' +
'}';
}
}