面对对象编程(中级部分)
idea的使用
IDEA 常用快捷键
- 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d
- 复制当前行, 自己配置 ctrl + alt + 向下光标
- 补全代码 alt + /
- 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】
- 导入该行需要的类 先配置 auto import , 然后使用 alt+enter 即可
- 快速格式化代码 ctrl + alt + L
- 快速运行程序 自己定义 alt + R
- 生成构造器等 alt + insert [提高开发效率]
- 查看一个类的层级关系 ctrl + H [学习继承后,非常有用]
- 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 [学继承后,非常有用]
- 自动的分配变量名 , 通过 在后面假 .var [老师最喜欢的]
import java.util.Scanner;
public class ArrayTest {
//使用IDEA开发一个Java项目testpro01,
// 创建一个类MyTools,编写一个方法,
// 可以完成对int数组冒泡排序的功能
public static void main(String[] agrs) {
Scanner scanner = new Scanner(System.in);
Scanner scanner1 = new Scanner(System.in);
//开始快捷键自动分配
int[] arr = {1, 3, 5, 4, 2, 8, 6};
MyTools mytool = new MyTools();
mytool.bubble(arr);
System.out.println("=====排序后的数组=====");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
class Percon {
String name;
int age;
//构造器-快捷键
public Percon(String name, int age) {
this.name = name;
this.age = age;
}
}
class MyTools {
public void bubble(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {//
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int key = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = key;
}
}
}
}
}
模板快捷键
file - > settings -> editor -> Live templates -> 可以查看有哪些快捷键 / 可以自己增加模板
模板可以高效的完成开发,提高速度
public class TestTemplate {
//main就是一个模板的快捷键
public static void main(String[] args) {
//sout模板快捷键
System.out.println();
//fori模板快捷键
for (int i = 0; i < ; i++) {
}
}
}
包
包的三大作用
- 区分相同名字的类
- 当类很多时,可以含好的管理类
- 控制访问范围
基本语法:
package com.hspedu;
- package 关键字,表示打包
- com.hspedu:表示包名
包的本质:实际上就是创建不同的文件来、目录保存类文件
package com.use;
import com.xiaoqiang.Dog;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();//对应开头引入的import的包名
System.out.println(dog);//com.xiaoqiang.Dog@14ae5a5
com.xiaoming.Dog dog1 = new com.xiaoming.Dog();
System.out.println(dog1);//com.xiaoming.Dog@7f31245a
}
}
包的命名
命名规则:
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键或者保留字
demo.class.exec1 错
demo.12a 错
demo.ab12.oa 对
命名规范:com.公司名.项目名.业务模块名
比如:com.hspedu.oa.model; com.hspedu.oa.controller;
- com.sina.crm.user(用户模块)
- com.sina.crm.order(订单模块)
- com.sina.crm.utils(工具类)
常用的包
- java.lang.* //lang包是基本包,默认引入,不需要再引入
- java.util.* //util 包,系统提供的工具包,工具类,使用Scanner
- java.net.* //网络包,网络开发
- java.awt.* //是做java的界面开发,GUI
如何引入包
package com.hspedu.pkg;
//注意:
//我们需要使用哪个类,就使用哪个类就好,不建议使用 *导入
import java.util.Arrays;
//import java.util.Scanner;//表示只会引入java.util 包下的Scanner
//import java.util.*;//表示将java.util 包下的所有类都导入
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.print(arr[i] + "\t");
}
}
}
包的注意事项
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
- import 指令 位置放在package 的下面,在类定义前面,可以有多句且没有顺序要求
//package 的作用是声明当前类所在的包,需要放在类的最上面,
//一个类中最多只有一句package
package com.hspedu.pkg;
//import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求
import java.util.Arrays;
import java.util.Scanner;
//类定义
public class PkgDetail {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] arr = {1,2,3,4};
Arrays.sort(arr);
}
}
类的定义仍待完善
package 包名;
class 类名{
成员变量、属性;
构造器;
成员方法;
}
访问修饰符
Java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
- 公开级别:用public 修饰,对外公开
- 受保护级别:用protected 修饰,对子类合同同一个包的类公开
- 默认级别:没有修饰符,向同一个包的类公开
- 私有级别:用private 修饰,只有类本身可以访问,不对外公开
注意事项:
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。
- 后面再讲子类的访问权限的问题
- 成员方法的访问规则和属性完全一样
面对对象编程三大特征
封装、继承、多态
封装
**封装(encapsulation)**就是把抽象出来的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作(对电视的操作就是典型的封装)
好处
- 隐藏实现细节 方法(连接数据库)< --调用(传入参数…)
- 可以对数据进行验证,保证安全合理
实现步骤(三步)
-
将属性进行私有化private 【不能直接修改属性】
-
提供一个公共的(public)set方法,用于对属性判断并修改
-
public void setXxx(类型 参数名){//Xxx表示某个属性 //加入数据验证的业务逻辑 属性 = 参数名; }
-
-
提供一个公共的(public)get方法,用于获取属性的值
-
public XX getXxx(){//权限判断 return xx; }
-
-
package com.hspedu.encap; public class Encapsulation01 { public static void main(String[] args) { //如果要使用快捷键Alt + r,需要先配置主类 //第一次使用鼠标点击形式运行,后面就可以用了 Person person = new Person(); person.setName("叶根春"); person.setAge(30); person.setSalary(30000); System.out.println(person.info()); //如果我们自己使用构造器指定属性 Person smith = new Person("smith", 2000, 50000); System.out.println("======smith的信息======"); System.out.println(smith.info()); } } /* 不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。 年龄合理就设置,否则给默认。年龄,必须在1 - 120之间 年龄,工资不能直接查看,name的长度在2 - 6字符之间 */ 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);//和this.setName(name)相当 setAge(age); setSalary(salary); } //自己写getXxx 和 setXxx 太慢,可以使用快捷键 //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; } }
封装练习
package com.hspedu.encap;
public class AccountTest {
public static void main(String[] args) {
Account account = new Account();
account.setName("jack");
account.setBalance(15);
account.setPassword("123456");
System.out.println("该账户的信息为:" + account.info());
}
}
/*
创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性。
1.Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)、
密码(必须是6位),如果不满足,则给出提示信息,并给默认值
2.通过setXxx的方法给Account的属性赋值。
3.在AccountTest中测试
*/
public class Account {
private String name;
private double balance;
private String password;
//提供构造器
public Account() {
}
public Account(String name, double balance, String password) {
this.setName(name);
this.setBalance(balance);
this.setPassword(password);
}
public String getName() {
return name;
}
public void setName(String name) {
int len = name.length();
if(len >=2 && len <= 4) {
this.name = name;
}else{
System.out.println("您的名字长度不符合,默认为空");
this.name = "";
}
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
if(balance > 20){
this.balance = balance;
}else{
System.out.println("您的余额小于20,默认为20");
this.balance = 20;
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
int len = password.length();
if(len == 6){
this.password = password;
}else {
System.out.println("您设置的密码长度不符合规定,默认为空");
this.password = "";
}
}
//显示账户信息
public String info(){
//可以增加一个权限校验
return "信息为:name=" + name + " 余额=" + balance + " 密码=" + password;
}
}
继承
可以解决代码复用,让我们的编程更靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可
继承的基本语法
class 子类 extends 父类{
}
- 子类会自动拥有父类定义的属性和方法
- 父类又叫 超类,基类
- 子类又叫派生类
代码的复用性提高了、代码的扩展性和维护性也提高了
继承的细节
-
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法去访问
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
-
public class ExtendsDetail { public static void main(String[] args) { System.out.println("====第一个对象===="); Sub sub = new Sub(); // sub.sayOk(); System.out.println("====第二个对象===="); Sub sub1 = new Sub("jack"); } } public class Base {//父类 //4个属性 public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; // public Base(){//无参构造器 // System.out.println("父类base()构造被调用...."); // } public Base(String name,int age){ System.out.println("父类Base(String name,int age)构造被调用...."); } //父类提供一个public的方法,返回n4 public int getN4(){ return n4; } public void test100(){ System.out.println("test100"); } protected void test200(){ System.out.println("test200"); } void test300(){ System.out.println("test300"); } private void test400(){ System.out.println("test400"); } public void callTest400(){//调用test400 test400(); } } public class Sub extends Base{//子类 public Sub(){//构造器 super("smith",10);//默认调用父类无参构造器 System.out.println("子类sub()构造器被调用...."); } //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器 public Sub(String name){ //do nothing super("tom",30); System.out.println("子类Sub()构造器被调用..."); } public void sayOk(){ //我们发现 父类的非private属性和方法 //都可以访问 //私有的属性和方法可以间接访问 System.out.println(n1 + " " + n2 + " " + n3); test100(); test200(); test300(); //test400();错误的 System.out.println("n4 = " + getN4()); callTest400(); } }
-
-
如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表);
-
super在使用时,必须放在构造器的第一行(只能在构造器中使用)
-
super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类, Object 是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
继承的本质
当子类创建好后,建立查找的关系
子类创建之后,从最顶级的父类依次向下挨个挨个创建属性。
子类访问属性(或者方法),从该子类开始依次向上查找该属性(或者方法),直到Object(不管该属性是否是私有,一旦找到就不再向上找;如果是私有的就不能直接访问)
package com.hspedu.extend_;
/*
*讲解继承的本质
*/
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.getAge());
System.out.println(son.hobby);
}
}
class GrandPa{//爷爷类
String name = "大头爷爷";
String hobby = "旅游";
int age = 89;
//当在Father类中找到了age之后就不会再到Grandpa找age
//GrandPa的age已经被Father的age挡住了,尽管这个age是私有的
}
class Father extends GrandPa{//父类
String name = "大头爸爸";
private int age = 39;
public int getAge(){//可以通过公共方法来间接访问父类的私有属性
return age;
}
}
class Son extends Father{//子类
String name = "大头儿子";
}
继承的练习
-
package com.hspedu.extend_.exercise; public class ExtendsExercise01 { 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"); } } //输出: //a //b name //b
-
package com.hspedu.extend_.exercise; import com.hspedu.modifier.A; public class ExtendsExercise02 { public static void main(String[] args) { C02 c = new C02(); } } class A02 { public A02() { System.out.println("我是A类"); } } class B02 extends A02 { public B02() { System.out.println("我是B类的无参构造器"); } public B02(String name) { System.out.println("我是B类的有参构造器"); } } class C02 extends B02 { public C02() { this("hello"); System.out.println("我是C类的无参构造器"); } public C02(String name) { super("hahah"); System.out.println("我是C类的有参构造器"); } } //输出: //我是A类 //我是B类的有参构造器 //我是C类的有参构造器 //我是C类的无参构造器
-
package com.hspedu.extend_.exercise; public class ExtendsExercise03 { public static void main(String[] args) { PC pc = new PC("intel",16,"500g","IBM"); pc.printInfo(); System.out.println("========="); NotePad notePad = new NotePad("intel",8,"256g","黑色"); notePad.printInfo(); } } /* *编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息 * 编写PC子类,继承Computer类,添加特有属性【品牌color】 * 编写NotePad子类,继承Computer类,添加特有属性【演示color】 * 编写Test类,在mian方法中创建PC和NotePad对象,分别给对象中特有的属性 * 赋值,以及从Computer类继承的属性赋值,使用方法并打印输出信息 */ class Computer{ //编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息 private String Cpu; private int memory; private String disk; public Computer(String cpu, int memory, String disk) { Cpu = cpu; this.memory = memory; this.disk = disk; } //用于返回Computer的详细信息 public String getDetails(){ return "CPU:" + Cpu + " memory:" + memory + " disk:" + disk; } //get set public String getCpu() { return Cpu; } public void setCpu(String cpu) { Cpu = cpu; } public int getMemory() { return memory; } public void setMemory(int memory) { this.memory = memory; } public String getDisk() { return disk; } public void setDisk(String disk) { this.disk = disk; } } class PC extends Computer{ //编写PC子类,继承Computer类,添加特有属性【品牌color】 private String brand; //这里IDEA 根据继承的规则,自动把构造器的调用写好 //这里也体现:继承设计的基本思想,父类的构造器完成父类属性初始化 //子类的构造器完成子类属性初始化 public PC(String cpu, int memory, String disk, String brand) { super(cpu, memory, disk); this.brand = brand; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public void printInfo(){ System.out.println("PC信息如下:"); //调用父类的getDetails方法,得到相关属性... System.out.println(getDetails() + " brand:" + brand); } } class NotePad extends Computer{ //编写NotePad子类,继承Computer类,添加特有属性【演示color】 private String color; public NotePad(String cpu, int memory, String disk, String color) { super(cpu, memory, disk); this.color = color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void printInfo(){ System.out.println("NotePad信息如下:"); //调用父类的getDetails方法,得到相关属性... System.out.println(getDetails() + " color:" + color); } }
super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法
- 访问父类的属性,但不能访问父类的private属性(super.属性名)
- 访问父类的方法,不能访问父类的private方法(super.方法名)
- 访问父类的构造器:super(参数列表); 只能放在构造器的第一句,只能出现一句
public class Super01 {
public static void main(String[] args) {
}
}
public class A{
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public A(){
}
public A(String name){
}
public A(int age){}
public void test100(){
}
protected void test200(){
}
void test300(){
}
private void test400(){
}
}
public class B extends A{
//1. 访问父类的属性,但不能访问父类的private属性(super.属性名)
public void hi(){
System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
}
//2.访问父类的方法,不能访问父类的private方法(super.方法名)
public void ok(){
super.test100();
super.test200();
super.test300();
}
//3.访问父类的构造器:super(参数列表); 只能放在构造器的第一句,只能出现一句
public B(){
super("jack");
}
}
super细节
-
调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
-
当子类中由父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super , this , 直接访问是一样的效果!(访问方法和子类访问属性的原理是类似的,不过super访问,没有查找本类的过程)
-
public class Super01 { public int n1 = 100; public static void main(String[] args) { B b = new B(); b.sum(); } } public class A{ public int n1 = 8; public void cal(){ System.out.println("A类的cal()方法..."); } } public class B extends A{ public void cal(){ System.out.println("B类的cal()方法..."); } public void sum(){ System.out.println("B类的sum()"); //希望调用父类——A 的cal方法 //这时,因为子类B没有cal方法,因此我们可以使用下面三种方式 //找cal方法时,顺序是:1.先找本类,如果有,则调用,如果没有就向上找 //如果父类有,并且可以调用,则调用 //如果父类没有就找父类的父类,直到Object //提示:如果查找方法的过程中,找到了该方法, //但是不能访问,就报错 cannot access // 如果查找方法的过程中,没有找到,则方法不存在 cal(); this.cal();//等价于cal(); super.cal();//直接向上找,跳过本类是否有cal() //演示访问属性的规则 //n1 和 this.n1 的查找规则和访问方法的规则是一样的 //1.先找本类,如果有,则调用 System.out.println(n1); System.out.println(this.n1); //本类没有就向上找,规则类似 //提示:如果查找属性的过程中,找到了该方法, //但是不能访问,就报错 cannot access // 如果查找属性的过程中,没有找到,则方法不存在 System.out.println(super.n1); } }
-
-
super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)都有同名的成员,使用super访问遵循就近原则。(也要遵守访问权限的相关规则)A ->B ->C
super 和 this的比较
类的定义完善
package 包名;
class 类名 extends 父类{
成员变量(属性);
构造方法(构造器);
成员方法(成员函数);
}
方法重写 / 覆盖(override)
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
方法重写的注意事项
-
子类方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。
-
子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类,比如:父类 返回类型是 Object,子类方法返回类型是String
-
public Object m1(){} public String m1(){}//String 是 Object 的子类
-
-
子类方法不能缩小父类方法的访问权限(但是可以扩大访问权限)
- public > proteced > 默认 > private
方法重写练习
-
public class OverrideExercise { public static void main(String[] args) { Student student = new Student("jack",17,10,97); Person person = new Person("tom",18); System.out.println(person.say()); System.out.println(student.say()); } } //1) 编写一个 Person 类,包括属性/private(name、age),构造器、方法 say(返回自我介绍的字符串)。 //2) 编写一个 Student 类,继承 Person 类,增加 id、score 属性/private, // 以及构造器,定义 say 方法(返回自我介绍的信息)。 //3) 在 main 中,分别创建 Person 和 Student 对象,调用 say 方法输出自我介绍 public class Person { // 编写一个 Person 类,包括属性/private(name、age), // 构造器、方法 say(返回自我介绍的字符串)。 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 + " age = " + age; } } public class Student extends Person{ // 编写一个 Student 类,继承 Person 类,增加 id、score 属性/private, // 以及构造器,定义 say 方法(返回自我介绍的信息)。 private int id; private double score; public Student(String name, int age, int id, double score) { super(name, age); this.id = id; this.score = score; } public int getId() { return id; } public void setId(int id) { this.id = id; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } public String say(){//方法重写 return super.say() + "\nid = " + id + " score = " + score; } }
多态
提高代码的复用性,更有利于代码的维护
多态:方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承的基础之上的
多态的具体体现
-
方法的多态(重载和重写就体现了多态)
-
public class PolyMethod { public static void main(String[] args) { //方法重载体现多态 A a = new A(); //这里我们传入不同的参数,就会调用不同的sum方法 //因此对sum方法来说,就是多种状态的体现 System.out.println(a.sum(10, 20)); System.out.println(a.sum(10, 20, 30)); //方法重写体现多态 B b = new B(); a.say(); b.say(); } } class B{//父类 public void say(){ System.out.println("B say() 方法被调用..."); } } class A extends B{//子类 public int sum(int n1, int n2){//和下面的sum 方法构成了重载 return n1 + n2; } public int sum(int n1, int n2, int n3){ return n1 + n2 + n3; } public void say(){ System.out.println("A say() 方法被调用..."); } }
-
-
对象的多态(核心)
-
一个对象的编译类型和运行类型可以不一致
-
编译类型在定义对象时,就确定了,不能改变
-
运行类型是可以变化的
-
编译类型看定义时 = 号 的左边, 运行类型看 = 号 的右边
-
Animal animal = new Dog();//animal 编译类型是 Aniamal,运行类型是Dog animal = new Cat();//animal 的运行类型变成了 Cat,编译类型仍然是 Animal
-
多态的注意事项
第一部分
多态的前提:两个对象(类)存在继承关系
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边,
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类中的特有成员;
- 最终运行效果看子类的具体实现
多态的向下转型
- 语法:子类类型 引用名 = (子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前要强转类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
package com.hspedu.poly_.detail_;
public class PolyDetail {
public static void main(String[] args) {
//向上转型:父类的引用指向了子类的对象
//语法:父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
Object obj = new Cat();//Object 也是 Cat的父类
//1. 可以调用父类中的所有成员(需遵守访问权限)
//2. 不能调用子类中的特有成员;
//因为在编译阶段,能调用哪些成员,是由编译类型来决定的,
// animal 不能调用 catchMouse方法
//3.最终运行效果看子类的具体实现
animal.eat();//猫吃鱼,调用方法时,从子类开始往上查找该方法
animal.run();
animal.show();
animal.sleep();
//现在希望可以调用Cat的,catchMouse方法
//多态的向下转型
//(1)语法:子类类型 引用名 = (子类类型)父类引用;
Cat cat = (Cat) animal;
cat.catchMouse();//现在cat的编译类型是Cat ,运行类型也是Cat
//(2)要求父类的引用必须指向的是当前目标类型的对象 、要
// 求原来的animal 本来就是指向 Cat 的
//Dog dog = (Dog)animal; 是错误的,
// 因为animal原来就是Cat类型,不能把Cat 变成其他子类类型
}
}
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.你好");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal{
}
第二部分
属性没有重写之说!属性的值看编译类型
package com.hspedu.poly_.detail_; public class PolyDetail02 { public static void main(String[] args) { //属性没有重写之说!属性的值看编译类型 Base base = new Sub();//向上转型 //访问属性直接看编译类型,也就是说不看运行类型 System.out.println("base 的count = " + base.count);//所以base.count = 10; Sub sub = new Sub(); System.out.println("sub 的count = " + sub.count);//输出为 20; } } class Base{//父类 int count = 10; } class Sub extends Base{//子类 int count = 20; }
第三部分
instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
package com.hspedu.poly_.detail_; public class PolyDetail03 { public static void main(String[] args) { BB bb = new BB(); //instanceOf 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型 System.out.println(bb instanceof AA); System.out.println(bb instanceof BB); AA aa = new BB(); //判断的是运行类型是否为XX或者XX类型的子类型 System.out.println(aa instanceof BB); System.out.println(aa instanceof AA); 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.hspedu.poly_.exercise_; public class PolyExercise01 { public static void main(String[] args) { double d = 13.4;//ok long l = (long) d;//ok System.out.println(l);//ok int in = 5;//ok boolean b = (boolean) int;//不对 Object obj = "hello";//ok,向上转型 String objStr = (String) obj;//可以,向下转型 System.out.println(objStr);//ok Object objPri = new Integer(5);//可以,向上转型 String str = (String) objPri; //错误ClassCastExcetpion,不能把指向Integer的父类引用,转成String Integer str1 = (Integer) objPri;//可以向下转型 } }
-
package com.hspedu.poly_.exercise_; public class polyExercise02 { public static void main(String[] args) { Sub sub = new Sub(); System.out.println(sub.count);//输出20 sub.display();//输出20 Base base = sub;//向上转型 System.out.println(base == sub);//输出true System.out.println(base.count);//输出10 base.display();//base的运行类型是Sub 调用Sub中的display的方法 } } class Base{ int count = 10; public void display(){ System.out.println(this.count); } } class Sub extends Base{ int count = 20; @Override public void display() { System.out.println(this.count); } }
java 的动态绑定机制(非常非常重要.)
- 当调用对象方法的时候,该方法会和该对象的内存地址、运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
package com.hspedu.poly_.dynamic_;
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();//向上转型
System.out.println(a.sum());//输出40
System.out.println(a.sum1());//输出30
System.out.println("==========");
//当子类的sum方法注销之后
//a的编译类型是A ,运行类型是B
System.out.println(a.sum());//输出30
//注销B中的sum1之后
System.out.println(a.sum1());//输出20
}
}
class A{
public int i = 10;
//动态绑定机制:
//1. 当调用对象方法的时候,该方法会和对象的内存地址、运行类型绑定
//2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {
return i;
}
// public int sum1() {
// return i + 10;
// }
}
多态的应用
-
多态数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
-
package com.hspedu.poly_.polyarr_; public class PolyArray { public static void main(String[] args) { Person[] people = new Person[5];//对象数组 people[0] = new Person("jack",10); people[1] = new Student("jack",18,85); people[2] = new Student("smith",19,30.1); people[3] = new Teacher("scott",30,200000); people[4] = new Teacher("king",50,25000); //调用每个对象的say()方法 //循环遍历对象数组 for (int i = 0; i < people.length; i++) { //person[i] 编译类型是Person,运行类型是根据实际情况由JVM来调用的 System.out.println(people[i].say());//动态绑定机制 //要调用特有的方法,用向下转型 if(people[i] instanceof Student){ ((Student) people[i]).study(); //这句等同于 这两句话 //Student student = (Student) people[i]; //student.study(); }else if(people[i] instanceof Teacher){ ((Teacher) people[i]).teach(); } } } } /* 1. 现有一个继承结构如下:要求创建1个Person对象, 2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象的say方法 2. 如何调用子类特有的方法,比如Teacher 有一个teach方法,Student有一个 study方法 */ 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 + "\t" + "age = " + age; } } 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() + "salary = " + salary; } //特有的方法 public void teach(){ System.out.println("老师:" + getName() + "正在授课..."); } } 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(int score) { this.score = score; } @Override public String say() { return super.say() + "score = " + score; } //特有的方法 public void study(){ System.out.println("学生:" + getName() + "正在上课..."); } }
-
-
**多态参数:**定义的形参类型为父类类型,实参类型允许为子类类型
-
前面的主人喂动物
-
package com.hspedu.poly_.polyparameter_; public class PolyParameter { public static void main(String[] args) { Worker tom = new Worker("Tom",3500); Manage milan = new Manage("Milan", 5000, 15000); Test test = new Test(); test.showEmpAnnual(tom); test.testWork(tom); test.showEmpAnnual(milan); test.testWork(milan); } } /* 定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual 的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage 方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法 测试类中添加一个方法showEmpAnnual(Employee),实现获取任何员工对象的年工资, 并在main方法中调用该方法[e.getAnnual()] 测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理, 则调用manage方法 */ package com.hspedu.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.hspedu.poly_.polyparameter_; public class Manage extends Employee{ private double bonus; public Manage(String name, double salary, double bonus) { super(name, salary); this.bonus = bonus; } public void manage(){ System.out.println("经理:" + getName() + "正在管理..."); } @Override public double getAnnual() { return super.getAnnual() + bonus; } } package com.hspedu.poly_.polyparameter_; public class Worker extends Employee{ public Worker(String name, double salary) { super(name, salary); } public void work(){ System.out.println("工人:" + getName() + "正在工作..."); } @Override public double getAnnual() {//因为普通员工没有其他的收入,就直接调用父类的方法 return super.getAnnual(); } } package com.hspedu.poly_.polyparameter_; public class Test { public void showEmpAnnual(Employee employee){ System.out.println(employee.getAnnual()); } public void testWork(Employee employee){ if(employee instanceof Worker){ ((Worker) employee).work();//向下转型 }else if (employee instanceof Manage){ ((Manage) employee).manage();//向下转型 } } } // 测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资, // 并在main方法中调用该方法[e.getAnnual()] // 测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理, // 则调用manage方法
-
Object类详解
equals方法
经典面试题:==和equals的对比
== 的左右两边只要有一边是基本数据类型,就是判断值是相等
package com.hspedu.object_; public class Equals01 { public static void main(String[] args) { A a = new A(); A b = a; A c = b; System.out.println(a == c);//true 两个对象的地址是否相等 B bObj = a; System.out.println(bObj == a);//true } } class B{ } class A extends B{ }
equals方法:
- equals:是Object类中的方法,只能判断引用类型,如何看Jdk源码
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String 【看String 和 Integer 的equals 源码】
//equals 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。 Integer integer1 = new Integer(1000); Integer integer2 = new Integer(1000); System.out.println(integer1 == integer2);//false System.out.println(integer1.equals(integer2));//true String str1 = new String("hspedu"); String str2 = new String("hspedu"); System.out.println(str1 == str2);//false System.out.println(str1.equals(str2));//true
equals重写练习
-
package com.hspedu.object_; public class EqualsExercise01 { public static void main(String[] args) { //判断两个Person对象的内容是否相等, // 如果两个Person对象的各个属性值都一样,则返回true,反之false Person person1 = new Person("jack", 10, '男'); Person person2 = new Person("smith", 10, '男'); //因为equals默认是判断地址是否相等,返回false //System.out.println(person1.equals(person2)); //重写之后,就变成了对内容的判断 System.out.println(person1.equals(person2)); } } class Person{ private String name; private int age; private char gender; //重写equals方法 public boolean equals(Object obj){ if(this == obj){//如果两个对象是同一个对象,则直接返回true return true; } if(obj instanceof Person){//如果obj是Person对象,再进行下一个阶段的判断 //向下转型,得到obj的各个属性 Person p = (Person) obj; if(this.name == p.name && this.age == p.age && this.gender == p.gender){ return true; }else{ return false; } }else{//如果不是Person类,则直接返回false 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; } }
-
package com.hspedu.object_; public class EqualsExercise02 { public static void main(String[] args) { Person p1 = new Person(); p1.name = "hspedu"; Person p2 = new Person(); p2.name = "hspedu"; System.out.println(p1 == p2);//false System.out.println(p1.name.equals(p2.name));//true System.out.println(p1.equals(p2));//false String s1 = new String("asdf"); String s2 = new String("asdf"); System.out.println(s1.equals(s2));//因为equals方法以及被重写了,true System.out.println(s1 == s2);//String 为引用数据类型 ,true } } class Person{ public String name; }
-
package com.hspedu.object_; public class EqualsExercise03 { public static void main(String[] args) { int it = 65; float fl = 65.0f; System.out.println("65和65.0f是否相等?" + (it == fl));//true char ch1 = 'A'; char ch2 = 12; System.out.println("65和‘A’是否相等?" + (it == ch1));//true System.out.println("12和ch2是否相等?" + (12 == ch2));//true String str1 = new String("hello"); String str2 = new String("hello"); System.out.println("str1和str2是否相等?" + (str1 == str2));//String是类 所以false System.out.println("str1是否equals str2?" + (str1.equals(str2)));//equals 方法被重写 true System.out.println("hello" == new java.sql.Date());//类型不一样,编译报错 } }
hashCode方法
-
提高具有哈希结构的容器的效率!
-
两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
-
两个引用,如果指向的是不同对象,则哈希值是不一样的
-
哈希值主要根据地址号来的!,但是不能完全将哈希值等价于地址
-
package com.hspedu.object_; public class HashCode_ { public static void main(String[] args) { AA aa = new AA(); AA aa1 = new AA(); AA aa2 = aa1; System.out.println("aa.hashCode() = " + aa.hashCode()); System.out.println("aa1.hashCode() = " + aa1.hashCode()); System.out.println("aa2.hashCode() = " + aa2.hashCode()); } } class AA{}
-
后面在集合中的hashCode 如果需要的话,也会重写
-
toString 方法
基本介绍:
默认返回:全类名(包名 + 类名) + @ + 哈希值的十六进制,【查看Object 的 toString 方法】子类往往重写toString 方法,用于返回对象的属性信息
重写toString 方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
package com.hspedu.object_; public class ToString_ { public static void main(String[] args) { /* Object 的toString() 源码 (1)getClass().getName() 类的全类名(包名 + 类名) (2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */ Monster monster = new Monster("小妖怪", "巡山的", 1000); System.out.println(monster.toString() + " hashCode = " + monster.hashCode()); //重写之后 System.out.println(monster); } } class Monster { private String name; private String job; private double sal; public Monster(String name, String job, double sal) { this.name = name; this.job = job; this.sal = sal; } //重写 toString方法,输出属性 //使用快捷键即可,alt + insert @Override public String toString() {//重写后一般是把对象的属性输出 return "Monster{" + "name='" + name + '\'' + ", job='" + job + '\'' + ", sal=" + sal + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } }
当直接输出一个对象时,toString 方法会被默认的调用:
System.out.println(monster);//相当于monster.toString
finalize方法
当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finnalize方法
垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc() 主动出发垃圾回收机制,测试:Car [name]
package com.hspedu.object_; //演示 Finalize的用法 public class Finalize_ { public static void main(String[] args) { Car car = new Car("宝马"); //置空,原本指向"宝马“对象的线,断掉了,垃圾回收器就会回收(销毁对象) //在销毁对象前,会调用该对象的finalize方法 //这时程序员就可以在finalize中,写自己的业务逻辑代码,(比如释放资源:数据库连接,或者打开文件..) //如果程序员不重写finalize方法,那么就会调用 Object类的finalize,即默认处理 //如果程序员重写了finalize,就可以实现自己的逻辑 car = null; //并不是,一个对象变成一个垃圾之后就会马上回收的,有一套算法来决定 System.gc();//主动调用垃圾回收机制 System.out.println("程序退出..."); } } class Car{ private String name; public Car(String name){ this.name = name; } //重写finalize @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("我们销毁了汽车:" + name); System.out.println("我们释放了某些资源..."); } }
实际开发中几乎不会用,仅仅为了应付面试
断点调试
在断点调试过程中,是运行状态,是以对象的运行类型来执行的
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调式到出错的代码行即显示错误,停下。进行分析从而找到这个Bug
- 断点调试是程序员必须掌握的技能
- 断点调试也能帮助我们查看Java底层源代码的执行过程,提高程序员的Java水平
快捷键
- F7(跳入):跳入方法内
- F8(跳出):逐行执行代码
- shift + F8(跳出):跳出方法
- F9:(resume,执行到下一个断点)
断点可以在debug过程中,动态的下断点
零钱通
使用Java开发零钱通项目,可以完成收益入账,消费,查看明细,退出系统等功能
界面:
编写文件 SmallChan geSys.java 完成基本功能(过程编程)
面对过程编程
-
先完成显示菜单,并可以选择
-
完成零钱通明细
-
完成收益入账
-
完成消费
-
package com.hspedu.smallchange; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; 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 details = "----------零钱通明细----------"; //3.完成收益入账 完成功能驱动程序员增加新的变化和代码 //定义新的变量 double money = 0; double balance = 0; Date date = null;//date 是 java.util.Date 类型,表示日期 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//可以用于日期格式化的 //4.完成消费 //(1)定义新变量保存消费的说明 String note = ""; do { System.out.println("==========零钱通菜单=========="); System.out.println("\t\t1 零钱通明细"); System.out.println("\t\t2 收益入账"); System.out.println("\t\t3 消费"); System.out.println("\t\t4 退 出"); System.out.print("请选择(1-4):"); key = scanner.next(); //使用switch 分支控制 switch (key){ case "1": System.out.println(details); break; case "2": System.out.println("收益入账的金额:"); money = scanner.nextDouble(); //money 的值范围应该校验 balance += money; //拼接收益入账信息到 details date = new Date();//获取当前的日期 details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t余额:" + balance; break; case "3": System.out.println("消费的金额:"); money = scanner.nextDouble(); //money的范围也应该校验 System.out.println("消费的说明:"); note = scanner.next(); balance -= money; //拼接消费的信息到details date = new Date();//获取当前的日期 details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额:" + balance; break; case "4": System.out.println("4 退 出"); loop = false; break; default: System.out.println("菜单选择有误,请重新选择"); } }while (loop); System.out.println("---------退出了零钱通---------"); } }
改进
- 用户输入4退出时,给出提示”你确定要退出吗?y/n” ,必须输入正确的 y/n ,否则循环输入指令,直到输入 y 或者 n。
- 在收益入账和消费时,判断金额是否合理,并给出相应的提示
- 将面对过程的代码修改成面对对象的方法,编写 SmallChangeSysOOP.java 类,并使用 SmallChangeSysApp.java 完成测试
package com.hspedu.smallchange.oop;
/**
*这里我们直接调用SmallChangeSysOOP 对象,显示主菜单
*/
public class SmallChangeSys {
public static void main(String[] args) {
SmallChangeSysOOP app = new SmallChangeSysOOP();
app.mainMenu();
}
}
package com.hspedu.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 = "";
//2.完成零钱通明细
//(1)可以把收益入账和消费,保存到数组
//(2)可以使用对象
//(3)简单的话可以使用String拼接
String details = "----------零钱通明细----------";
//3.完成收益入账 完成功能驱动程序员增加新的变化和代码
//定义新的变量
double money = 0;
double balance = 0;
Date date = null;//date 是 java.util.Date 类型,表示日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//可以用于日期格式化的
//4.完成消费
//(1)定义新变量保存消费的说明
String note = "";
//先显示主菜单,并可以选择
public void mainMenu(){
do {
System.out.println("\n==========选择零钱通菜单(OOP)==========");
System.out.println("\t\t 1 零钱通明细");
System.out.println("\t\t 2 收益入账");
System.out.println("\t\t 3 消费");
System.out.println("\t\t 4 退 出");
System.out.print("请选择(1-4):");
key = scanner.next();
//使用switch 分支控制
switch (key){
case "1":
this.detail();//调用显示细节方法
break;
case "2":
this.income();//调用收益入账方法
break;
case "3":
//调用消费方法
this.pay();
break;
case "4":
//调用退出方法
this.exit();
break;
default:
System.out.println("菜单选择有误,请重新选择");
}
}while (loop);
}
//完成零钱通明细
public void detail(){
System.out.println(details);
}
//完成收益入账
public void income(){
System.out.println("收益入账的金额:");
money = scanner.nextDouble();
//money 的值范围应该校验,编程思想:
//先找出不正确的金额的条件,然后给出提示,就直接return
if(money <=0 ){
System.out.println("收益入账金额应该大于 0");
return;
}
balance += money;
//拼接收益入账信息到 details
date = new Date();//获取当前的日期
details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
}
//完成消费
public void pay(){
System.out.println("消费的金额:");
money = scanner.nextDouble();
//money的范围也应该校验
if(money <= 0 || money > balance){
System.out.println("你的消费金额应该在0-" + balance + "之间");
return;
}
System.out.println("消费的说明(消费地点、用途):");
note = scanner.next();
balance -= money;
//拼接消费的信息到details
date = new Date();//获取当前的日期
details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
}
//退出方法
public void exit(){
//(1)定义一个变量,choice 接受用户的输入
//(2)使用 while + break,
//(3)退出while之后,再判断choice是y还是n,
//(4)建议一段代码完成一个小功能,尽量不要混在一起
String choice = "";
while (true){
System.out.println("你确定要退出吗? y/n");
choice = scanner.next();
if(choice.equals("y") || choice.equals("n")){
break;
}else{
System.out.println("您的输入有误,请重新输入");
}
}
//当退出while,进行判断
if (choice.equals("y")){
loop = false;
}
}
}
本章的作业
1.
-
package com.hspedu.homework; /** * 1.定义一个Person类{name,age,job},初始化Person对象数组, * 有3个person对象,并按照age从大到小 进行排序 * 使用冒泡排序 */ public class Homework01 { public static void main(String[] args) { Person[] people = new Person[3]; people[0] = new Person("jack",30,"打工"); people[1] = new Person("smith",34,"公务员"); people[2] = new Person("tom",18,"大学生"); for (int i = 0; i < people.length - 1; i++) { for (int j = 0; j < people.length - i -1; j++) { //按照age从大到小 进行排序 if(people[j].getAge() < people[j + 1].getAge()){ Person person = null; person = people[j]; //先将people[j]的引用置空 people[j] = null; //再让people[j]指向people[j + 1]的对象 people[j] = people[j + 1]; people[j + 1] = null;//和上面同理 people[j + 1] = person; } } } 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() { } public Person(String name, int age, String job) { this.name = name; this.age = age; this.job = job; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", 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; } }
2.
-
package com.hspedu.homework; /** * 写出四种访问修饰符各各自的权限 * (1) public 同一个类 同一个包 不同类 不同包 * (2) protected 同一个类 同一个包 不同类 * (3) 默认 同一个类 同一个包 * (4) private 同一个类 */ public class Homework02 { public static void main(String[] args) { } }
3.
-
package com.hspedu.homework; /** * 编写老师类 * (1)要求有属性“姓名name”,“年龄age”,“职称post”,“基本工资salary” * (2)编写业务方法,introduce(),实现输出一个教师的信息。 * (3)编写教师类的三个子类:教授类(Professor),副教授类,讲师类。工资级别分别为: * 教授为1.3、副教授为1.2、讲师为1.1。在三个子类里面都重写父类的introduce()方法。 * (4)定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印。 */ public class Homework03 { public static void main(String[] args) { Professor professor = new Professor("贾宝玉", 28, "高级职称", 20000, 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; } //编写业务方法,introduce(),实现输出一个教师的信息 public void introduce(){ System.out.print("name:" + name + " age:" + age + " post:" + post + " salary:" + salary); } 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; } } //编写教师类的三个子类:教授类(Professor),副教授类,讲师类。 // 工资级别分别为 教授为1.3、副教授为1.2、讲师为1.1。 // 在三个子类里面都重写父类的introduce()方法。 class Professor extends Teacher{ public Professor(String name, int age, String post, double salary,double grade) { super(name, age, post, salary,grade); } public void introduce() { System.out.println(" 这是教授的信息:"); super.introduce(); } } class AP extends Teacher{ public AP(String name, int age, String post, double salary,double grade) { super(name, age, post, salary,grade); } public void introduce() { System.out.println(" 这是副教授的信息:"); super.introduce(); } } class Instruction extends Teacher{ public Instruction(String name, int age, String post, double salary,double grade) { super(name, age, post, salary,grade); } public void introduce() { System.out.println(" 这是讲师的信息:"); super.introduce(); } }
4.
-
package com.hspedu.homework; /** * 通过继承实现员工工资核算打印功能 * 父类:员工类 * 子类:部门经理类、普通员工类 * 1. 部门经理工资 = 1000 + 单日工资 * 天数 * 等级(1.2) * 2. 普通员工工资 = 单日工资 * 天数 * 等级(1.0) * 3. 员工属性:姓名,单日工资,工作天数 * 4. 员工方法(打印工资) * 5. 普遍员工及部门经理都是员工子类,需要重写打印工资方法 * 6. 定义并初始化普通员工对象,调用打印工资方法输出工资,定义并初始化部门经理对象 * 调用打印工资方法输出工资 */ public class Homework04 { public static void main(String[] args) { Manager manager = new Manager("刘备", 100, 20, 1.2); //设置奖金 manager.setBonus(3000); //打印 manager.printSal(); Worker worker = new Worker("张飞", 90, 24, 1.0); //打印 worker.printSal(); } } class Employee { private String name; private double daySal; private int workDays; //分析出还有一个属性等级 private double grade; public Employee(String name, double daySal, int workDays, double grade) { this.name = name; this.daySal = daySal; this.workDays = workDays; this.grade = grade; } //员工方法(打印工资)单日工资 * 天数 * 等级(1.0) public void printSal(){ System.out.println( name + "的工资为:" + getDaySal() * getWorkDays() * getGrade()); } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getDaySal() { return daySal; } public void setDaySal(double daySal) { this.daySal = daySal; } public int getWorkDays() { return workDays; } public void setWorkDays(int workDays) { this.workDays = workDays; } public double getGrade() { return grade; } public void setGrade(double grade) { this.grade = grade; } } class Manager extends Employee{ private double bonus; //创建manager对象时奖金是多少是不确定的,所以在构造器中先不给bonus //可以通过setBonus来设置 public Manager(String name, double daySal, int workDays, double grade) { super(name, daySal, workDays, grade); } //重写父类的方法 部门经理工资 = 1000 + 单日工资 * 天数 * 等级(1.2) @Override public void printSal() { //因为经理的工资计算和Employee不一样,直接完全重写 System.out.println("经理" + getName() + "的工资为:" + (getDaySal() * getWorkDays() * getGrade() + getBonus())); } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } } class Worker extends Employee{ public Worker(String name, double daySal, int workDays, double grade) { super(name, daySal, workDays, grade); } @Override public void printSal() { super.printSal(); } }
5.
package com.hspedu.homework;
/**
* 设计一个父类 员工类,子类:工人类worker,
* 农民类Peasant,教师类,科学家类Scientist,服务生类Waiter,
* 1. 其中工人,农民,服务生,只有基本工资
* 2. 教师除基本工资外,还有课酬(元/天)
* 3. 科学家除基本工资外,还有年终奖
* 编写一个测试类,将各种类型的员工的全年工资打印出来
*/
public class Homework05 {
public static void main(String[] args) {
}
}
class Employee1 {
private String name;
private double basicSal;
public Employee1(String name, double basicSal) {
this.name = name;
this.basicSal = basicSal;
}
//打印全年工资
public void printSal(){
System.out.println("全年工资为:" + getBasicSal());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getBasicSal() {
return basicSal;
}
public void setBasicSal(double basicSal) {
this.basicSal = basicSal;
}
}
class Teacher1 extends Employee1{
private double daySal;
public Teacher1(String name, double basicSal, double daySal) {
super(name, basicSal);
this.daySal = daySal;
}
//重写打印
@Override
public void printSal() {
System.out.println("教师的年工资为:" + (getBasicSal() + getDaySal() * ));
}
public double getDaySal() {
return daySal;
}
public void setDaySal(double daySal) {
this.daySal = daySal;
}
}
6.
package com.hspedu.homework;
/**
* 假定Gand,Father,Son在同一个包,问:
* 父类和子类中通过this和super都可以调用哪些方法和属性
*/
public class Homework06 {
public static void main(String[] args) {
}
}
class Grand{
String name = "AA";
private int age = 100;
public void g1(){}
}
class Father extends Grand{
String id = "001";
private double score;
public void f1(){
//super可以访问哪些成员(属性和方法)?
// g1()方法,super.name
//this可以访问哪些成员?
// id,score属性 f1()方法 this.name this.g1()
}
}
class Son extends Father{
String name = "BB";
public void g1(){}
private void show(){
//super可以访问哪些成员(属性和方法)?
// g1(),f1()方法 super.name super.id
//this可以访问哪些成员?
// 本类的name属性 g1(),show()方法 this,id this.f1()
}
}
7.
package com.hspedu.homework;
/**
* 写出程序的结果
*/
public class Homework07 {
}
class Test{
String name = "Rose";
Test(){
System.out.println("Test");
}
Test(String name){
this.name = name;//这里把父类的 name 改成了 john
}
}
class Demo extends Test{
String name = "Jack";
Demo(){
super();
System.out.println("Demo");
}
Demo(String s){
super(s);
}
public void test(){
System.out.println(super.name);
System.out.println(this.name);
}
public static void main(String[] args) {
//匿名对象
new Demo().test();//输出 Test Demo Rose Jack
new Demo("john").test();//输出 John jack
}
}
8.
package com.hspedu.homework;
/**
* 1,在上面的基础上扩展新类CheckingAccount对每次存款和取款都收取1美元的手续费
* 2,扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生
* (earnMonthlyInterrest方法被调用),并且有每个月三次免手续费的存款或取款。
* 在earnMonthlyInterest方法中重置交易计数
* 3,体会重写的好处
*/
public class Homework08 {
public static void main(String[] args) {
// CheckingAccount checkingAccount = new CheckingAccount(1000);
// checkingAccount.deposit(10);// 1010 - 1 = 1009
// System.out.println(checkingAccount.getBalance());
//测试SavingsAccount方法
SavingsAccount savingsAccount = new SavingsAccount(1000);
savingsAccount.deposit(100);
savingsAccount.deposit(100);
savingsAccount.deposit(100);
System.out.println(savingsAccount.getBalance());
savingsAccount.deposit(100);
System.out.println(savingsAccount.getBalance());//1400 - 1 = 1399
//月初,定时器自动调用一下 earnMonthlyInterest
savingsAccount.earnMonthlyInterest();
System.out.println(savingsAccount.getBalance());//余额加了多少钱1399 + 13.99 * 0.01
savingsAccount.withdraw(100);//取款100 免手续费
System.out.println(savingsAccount.getBalance());
}
}
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;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
class CheckingAccount extends BankAccount{//新的账号
public CheckingAccount(double initialBalance) {
super(initialBalance);
}
@Override
public void deposit(double amount) {
super.deposit(amount - 1);//巧妙地使用了父类的deposit
//1 块钱入银行的账号
}
@Override
public void withdraw(double amount) {
super.withdraw(amount + 1);
//1 块钱入银行的账号
}
}
//扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生
//(earnMonthlyInterrest方法被调用),并且有每个月三次免手续费的存款或取款。
//在earnMonthlyInterest方法中重置交易计数
class SavingsAccount extends BankAccount{
//增加新属性
private int count = 3;
private double rate = 0.01;//利率
public void earnMonthlyInterest(){//每个月初,我们统计上个月的利息,同时将count = 3
count = 3;//
super.deposit(getBalance() * rate);
}
@Override
public void deposit(double amount) {
//判断是否还可以免手续费
if(count > 0){
super.deposit(amount);
}else {
super.deposit(amount - 1);
}
count--;//减去一次
}
@Override
public void withdraw(double amount) {
if(count > 0){
super.withdraw(amount);
}else {
super.withdraw(amount + 1);
}
count--;//减去一次
}
public SavingsAccount(double initialBalance) {
super(initialBalance);
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
}
9.
package com.hspedu.homework;
/**
* 设计一个Point类,其x和y坐标可以通过构造器提供。
* 提供一个子类LabeledPoint,其构造器接受一个标签值和x,y坐标,
* 比如:new LabeledPoint("Balck Thursday" ,1929,230.07),写出对应的构造器即可
*/
public class Homework09 {
public static void main(String[] args) {
LabeledPoint labeledPoint = new LabeledPoint("Balck Thursday", 1929, 230.07);
}
}
class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
class LabeledPoint extends Point{
//属性
private String label;
//方法
public LabeledPoint(String label,double x, double y ) {
super(x, y);
this.label = label;
}
}
10.
package com.hspedu.homework;
/**
* 编写Doctor类{name,age,job,gender,sal}
* 相应的getter()和setter()方法,5个参数的构造器,
* 重写父类的equals()方法:public boolean equals(Object obj),
* 并判断测试类中创建的两个对象是否相等。相等就是判断属性是否相同
*/
public class Homework10 {
public static void main(String[] args) {
}
}
class Doctor {
private String name;
private int age;
private String job;
private String gender;
private double sal;
public boolean equals(Object obj){
if(this == obj){//如果是同一个对象
return true;
}else if(obj instanceof Doctor){//不是同一个对象
//向下转型,得到obj的各个属性
Doctor doctor1 = (Doctor) obj;
if(this.name == doctor1.name && this.age == doctor1.age
&& this.job == doctor1.job && this.gender == doctor1.gender
&& this.sal == doctor1.sal){
return true;
}else {//如果不是Doctor类的
return false;
}
}else {
return false;
}
}
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;
}
}
11.
package com.hspedu.homework;
public class Homework11 {
public static void main(String[] args) {
Person02 student = new Student();
//向上转型
//可以调用父类中的所有成员(需遵守访问权限)
//不能调用子类中的特有成员,可以调用子类重写的方法,动态绑定机制
//最终运行效果看子类的具体实现
student.run();
student.eat();
//可以调用子类类型中所有的成员
Student student1 = (Student) student;
student1.run();
student1.study();
student1.eat();
}
}
class Person02 {
public void run(){
System.out.println("person run");
}
public void eat(){
System.out.println("person eat");
}
}
class Student extends Person02 {
@Override
public void run() {
System.out.println("student run");
}
public void study(){
System.out.println("student study..");
}
}
12.
//写出== 和 equals 的区别
//== 是比较运算符 可以判断基本数据类型,判断值是否相等 可以判断引用类型,判断两个对象是否指向同一个对象
//equals 是Object类的方法 Java类都可以用equals 不可以用于判断基本数据类型 可以判断引用类型,判断两个对象的地址是否相等,但是子类往往重写该方法,比较对象属性是否相等,比如:String int
13.
14.
package com.hspedu.homework.Homework13_;
public class Homework14 {
public static void main(String[] args) {
C c = new C();
//输出
//我是A类
//hahah我是B类的有参构造器
//我是C类的有参构造器
//我是C类的无参构造器
}
}
class A{
public A(){
System.out.println("我是A类");//(1)
}
}
class B extends A{
public B(){
System.out.println("我是B类的无参构造器");
}
public B(String name){
System.out.println(name + "我是B类的有参构造器");//(2)
}
}
class C extends B{
public C(){
this("hello");
System.out.println("我是C类的无参构造器");//(4)
}
public C(String name){
super("hahah");
System.out.println("我是C类的有参构造器");//(3)
}
}
15.
什么是多态,多态的具体体现有哪些?
多态:方法或对象具有多种形态,是OOP的第三大特征,是建立在封装和继承的基础之上的。
多态的具体体现:
- 方法多态:
- 重载体现多态
- 重写体现多态
- 对象多态
- 对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定,不能变化
- 对象的运行类型是可以变化的,可以通过getClass() 方法来查看运行类型
- 编译类型看定义时 = 的左边 ,运行类型是看 = 的右边
16.
Java的动态绑定机制是什么?
- 当调用对象的方法时,该方法会与该对象的内存地址 / 运行类型绑定
- 当调用对象的属性时,没有动态绑定机制,哪里声明,哪里使用
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;
}
}
### 11.
```java
package com.hspedu.homework;
public class Homework11 {
public static void main(String[] args) {
Person02 student = new Student();
//向上转型
//可以调用父类中的所有成员(需遵守访问权限)
//不能调用子类中的特有成员,可以调用子类重写的方法,动态绑定机制
//最终运行效果看子类的具体实现
student.run();
student.eat();
//可以调用子类类型中所有的成员
Student student1 = (Student) student;
student1.run();
student1.study();
student1.eat();
}
}
class Person02 {
public void run(){
System.out.println("person run");
}
public void eat(){
System.out.println("person eat");
}
}
class Student extends Person02 {
@Override
public void run() {
System.out.println("student run");
}
public void study(){
System.out.println("student study..");
}
}
12.
//写出== 和 equals 的区别
//== 是比较运算符 可以判断基本数据类型,判断值是否相等 可以判断引用类型,判断两个对象是否指向同一个对象
//equals 是Object类的方法 Java类都可以用equals 不可以用于判断基本数据类型 可以判断引用类型,判断两个对象的地址是否相等,但是子类往往重写该方法,比较对象属性是否相等,比如:String int
13.
14.
package com.hspedu.homework.Homework13_;
public class Homework14 {
public static void main(String[] args) {
C c = new C();
//输出
//我是A类
//hahah我是B类的有参构造器
//我是C类的有参构造器
//我是C类的无参构造器
}
}
class A{
public A(){
System.out.println("我是A类");//(1)
}
}
class B extends A{
public B(){
System.out.println("我是B类的无参构造器");
}
public B(String name){
System.out.println(name + "我是B类的有参构造器");//(2)
}
}
class C extends B{
public C(){
this("hello");
System.out.println("我是C类的无参构造器");//(4)
}
public C(String name){
super("hahah");
System.out.println("我是C类的有参构造器");//(3)
}
}
15.
什么是多态,多态的具体体现有哪些?
多态:方法或对象具有多种形态,是OOP的第三大特征,是建立在封装和继承的基础之上的。
多态的具体体现:
- 方法多态:
- 重载体现多态
- 重写体现多态
- 对象多态
- 对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定,不能变化
- 对象的运行类型是可以变化的,可以通过getClass() 方法来查看运行类型
- 编译类型看定义时 = 的左边 ,运行类型是看 = 的右边
16.
Java的动态绑定机制是什么?
- 当调用对象的方法时,该方法会与该对象的内存地址 / 运行类型绑定
- 当调用对象的属性时,没有动态绑定机制,哪里声明,哪里使用