Java教程

Java中阶(1)

本文主要是介绍Java中阶(1),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

    • IDEA使用
      • 设置字体
      • 字符编码
      • 快捷键
      • 模板
      • 包的作用
      • 包的基本语法
      • 包的本质
      • 包的命名规则和规范
        • 规则
        • 规范
      • 常用的包
      • 包的使用细节
    • 访问修饰符
      • 介绍
      • 使用注意事项
    • OOP三大特征(封装,继承和多态)
      • 封装
        • 实现步骤
      • 继承
        • 基本语法
        • 注意和细节
        • 继承的本质
    • Super关键字
      • 基本语法
      • 细节
      • super和this的比较
    • 方法重写(overwrite)
      • 注意事项
      • 方法重写和方法重载的比较
    • 多态
      • 具体体现
        • 方法的多态
        • 对象的多态
      • 注意事项和细节
      • Java动态绑定机制(非常非常重要)
      • 多态数组
      • 多态参数
    • Object类详解,垃圾回收机制
      • equals方法
        • == 和equals的对比
      • hashCode方法
      • toString方法
      • finalize方法
    • 断点调试(Debug)
    • 项目

IDEA使用

  • IDEA是JetBrains公司的产品,总部位于捷克的首都布拉格
  • 可用于Java、HTML、CSS、PHP、MySQL、Python等
  • 面向项目

设置字体

  • 项目字体大小:file-----settings-----Appearance&Bahavior-------Appearance------Use custom font

  • 代码字体大小:file-----settings-----Editor-------Font

  • 代码字体粗体:file-----settings-----Editor------Color Scheme-----General------Text-----Default text----Bold

  • 背景颜色:file-----settings-----Editor------Color Scheme-----Scheme

字符编码

  • file-----settings-----Editor------File Encodings(默认是UTF-8)

快捷键

  • File------settings-----Keymap-----搜一下delete 等
  • 删除当前行,ctrl + d(自己配置)
  • 复制当前行,ctrl + alt + 向下光标(自己配置)
  • 补全代码 alt + /
  • 添加注释和取消注释 ctrl + /(第一次是添加注释,第二次是取消注释)
  • 导入该行需要的类,先配置file-----settings-----Editor-----General-----Auto Import,然后使用alt + enter 即可
  • 快速格式化代码 ctrl + alt + L
  • 快速运行程序 自己定义alt + R
  • 生成构造方法等alt + insert [提高开发效率]
  • 查看一个类的层级关系 ctrl + H 【学习继承后,非常有用】
  • 将光标放在一个方法上,输入ctrl + B,可以选择定位到方法【学习继承后,非常有用】
  • 自动的分配变量名,通过 在后面加 .var

模板

  • file----settings----editor----live templates 可以查看有哪些模板快捷键,也可以增加模板

包的作用

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围

包的基本语法

package 包名; package 关键字,表示打包。

包的本质

​ 实际上就是创建不同的文件夹/目录来保存类文件。

在这里插入图片描述

包的命名规则和规范

规则

  • 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字。

规范

  • 一般是小写字母+小圆点一般是com.公司名.项目名.业务模块名

常用的包

  • java.lang.* 默认引用,不用再引用
  • java.util.* 工具包
  • java.net.* 网络包
  • java.awt.* 界面开发GUI

包的使用细节

  • 如何引入包:语法:import 包;引入包的主要目的就是使用该包下的类
  • 最好是需要使用什么类就导入哪个类
  • package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
  • import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求

访问修饰符

介绍

四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限

  • 公开级别:public 修饰,对外公开
  • 受保护级别:protected 修饰,对子类和同一个包中的类公开
  • 默认级别:没有修饰符号,向同一个包的类公开
  • 私有级别:private 修饰,只有类本身可以访问,不对外公开
访问级别访问控制修饰符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符号××
私有private×××

使用注意事项

  • 修饰符可以用来修饰类中的属性,成员方法以及类
  • 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
  • 成员方法的访问规则和属性完全一样

OOP三大特征(封装,继承和多态)

封装

封装(encapsulation)就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作【方法】,才能对数据进行操作。

(1)隐藏实现细节

(2)可以对数据进行验证,保证安全合理

实现步骤

  • 将属性进行私有化private【不能直接修改属性】
  • 提供一个公共public的set方法,用于对属性判断并赋值
  • 提供一个公共public的get方法,用于获取属性的值
  • 使用快捷键写set和get方法,alt + insert,选择Getter and Setter,然后根据要求完善代码。
  • 如果要使用快捷键运行,需要先配置一下主类,第一次run一下,后面就有了
//构造器和set结合
class Person{
    public String name;
    private int age;
    private double salary;
    public Person(){     
    }
    public Person(String name,int age,double salary){
        setName(name);//等价于this.setName(name);
        setAge(age);//等价于this.setAge(age);
        setSalary(salary);//等价于this.setSalary(salary);
    }
    public void setName(String name){};
    public void setAge(int age){};
    public void setSalary(double salary){};
    public String getName(){};
    public int getAge(){};
    public double getSalary(){};
}

如何去掉参数提示:file-----settings-----Editor-----Inlay Hints-----Java-----Parameter hints-----show parameter hints for:

继承

继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

基本语法

  • class 子类 extends 父类{}
  1. 子类就会自动拥有父类定义的属性和方法
  2. 父类又叫超类,基类
  3. 子类又叫派生类

在这里插入图片描述

注意和细节

  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
  • 子类必须调用父类的构造器,完成父类的初始化,super()先调用父类再调用子类
  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
  • 如果希望指定去调用父类的某个构造器,则显式的调用一下:super()
  • super在使用时,需要放在构造器第一行,super()只能在构造器中使用
  • super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
  • Java所有类都是Object的子类,Object是所有类的基类
  • 父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)
  • 子类最多只能继承一个父类(指直接继承),即Java中是单继承机制
  • 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系 Person is a Music?cat is an animal

继承的本质

当子类对象创建好后,建立查找的关系

在这里插入图片描述

如果利用son对象访问age这个属性,而Father类的age属性是private的,Grandpa类的age属性是非私有的,那么son在访问的时候查到Father类的age属性是私有的会直接报错,而不会继续向上查找Grandpa类的age属性。

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");
    }
}

B b = new B();//输出 a换行b name换行b
//注意点:this 和 super 不能共存在一个构造器
//父类的构造器完成父类属性初始化,子类的构造器完成子类属性初始化

Super关键字

super代表父类的引用,用于访问父类的属性、方法、构造器

基本语法

  • 访问父类的属性,但不能访问父类的private属性,super.属性名
  • 访问父类的方法,但不能访问父类的private方法,super.方法名(参数列表)
  • 访问父类的构造器,super(参数列表),只能放在构造器的第一句,只能出现一句。

细节

  • 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)

  • 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super,如果没有重名,使用super、this、直接访问是一样的效果。【this、直接访问】找方法时,先找本类再找父类,如果查找方法的过程中,找到了但是不能访问就报错,如果没找到,则提示方法不存在。而【super.方法】是直接跳过找本类,从找父类开始。

  • super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

super和this的比较

No.区别点thissuper
1访问属性访问本类中的属性,如果本类没有此属性则从父类中继续查找从父类开始查找属性
2调用方法访问本类中的方法,如果本类没有此方法则从父类中继续查找从父类开始查找方法
3调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在子类构造器的首行
4特殊表示当前对象子类中访问父类对象

方法重写(overwrite)

方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法。

注意事项

  • 子类的方法的形参列表,方法名称,要和父类方法的参数,方法名称完全一样;
  • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类。比如【父类】返回类型是Object,【子类】方法返回类型是String
  • 子类方法不能缩小父类方法的访问权限

方法重写和方法重载的比较

名称发生范围方法名参数列表返回类型修饰符
重载overload本类必须一样类型,个数或者顺序至少有一个不同无要求无要求
重写override父子类必须一样相同子类重写方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类子类方法不能缩小父类方法的访问权限

多态

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

具体体现

方法的多态

  • 方法重写和重载就体现多态

对象的多态

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就确定了,不能改变
  • 运行类型是可以变化的
  • 编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边
public class Animal{
    public void cry(){
        System.out.println("动物在叫····");
    }
}

public class Cat extends Animal{
    public void cry(){
        System.out.println("猫在叫 喵喵喵");
    }
}

public class Dog extends Animal{
    public void cry(){
        System.out.println("狗在叫 汪汪汪");
    }
}

//主类
Animal animal = new Dog();//编译类型是Animal,运行类型是Dog
animal.cry();//因为运行时,执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry
//输出狗在叫 汪汪汪
animal = new Cat();//编译类型是Animal,运行类型是Cat
animal.cry();//因为运行时,执行到该行时,animal运行类型是Cat,所以cry就是Cat的cry
//输出猫在叫 喵喵喵

喂食问题改进

public class Master{
    private String name;
    //animal的编译类型是Animal,运行类型可以是Animal子类的对象
    //food的编译类型是Food,运行类型可以是Food子类的对象
    public void feed(Animal animal,Food food){
        System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
    }
}

注意事项和细节

  • 多态的前提是:两个对象(类)存在继承关系
  • 多态的向上转型
    • 本质:父类的引用指向了子类的对象
    • 语法:父类类型 引用名 = new 子类类型()
    • 编译类型看左边,运行类型看右边
      • 可以调用父类中的所有成员(需遵守访问权限)
      • 不能调用子类的特有的成员(因为在编译阶段能调用哪些成员是由编译类型决定)
      • 最终运行的效果看子类的具体实现(调用的时候先在子类中找,再在父类中找)
  • 多态的向下转型
    • 语法 子类类型 引用名 = (子类类型)父类引用
    • 只能强转父类的引用,不能强转父类的对象
    • 要求父类的引用必须指向的是当前目标类型的对象
    • 可以调用子类类型中所有的成员
  • 属性没有重写之说!属性的值看编译类型
  • instanceOf 比较操作符,用于判断对象的类型是否为××类型或××类型的子类型(运行机制)
class AA{}
class BB extends AA{}
//主函数
BB bb = new BB();
System,out.println(bb instanceof BB);//true
System,out.println(bb instanceof AA);//true
AA aa = new BB();
System,out.println(aa instanceof BB);//true
System,out.println(aa instanceof AA);//true
//说明instanceof判断的是运行类型
Object obj = new Object();
System,out.println(obj instanceof AA);//false
String str = "hello";
System,out.println(str instanceof Object);//true
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);
    }
}
//主函数
Sub s = new Sub();
System,out.println(s.count);//属性看编译类型,输出20
s.display();//从运行类型即子类开始寻找方法,输出20
Base b = s;//向上转型
System,out.println(b == s);//比较的是指向对象的地址,虽然编译类型不同,但指向同一对象,输出true
System,out.println(b.count);//属性看编译类型,输出10
b.display();//从运行类型即子类开始寻找方法,输出20

Java动态绑定机制(非常非常重要)

  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
class A{
    public int i = 10;
    public int sum(){
        return getI() + 10;//调用哪个getI(),和运行类型有关,调用B类的getI()
    }
    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 sum1(){
    //    return i + 10;
    //}
    public int getI(){
        return i;
    }
}

A a = new B();//向上转型
System.out.println(a.sum());//--------30
System.out.println(a.sum1());//-------20

多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

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 + "\t" + 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;
    }
    //写重写父类的say方法
    public String say() {
        return "老师 " + super.say() + " salary=" + salary;
    }
    //特有方法
    public void teach() {
        System.out.println("老师 " + getName() + " 正在讲java课程...");
    }
}

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;
    }
    //重写父类say
    public String say() {
        return "学生 " + super.say() + " score=" + score;
    }
    //特有的方法
    public void study() {
        System.out.println("学生 " + getName() + " 正在学java...");
    }
}

//主函数
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);

//循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
    //person[i] 编译类型是 Person ,运行类型是是根据实际情况有JVM来判断
    System.out.println(persons[i].say());//动态绑定机制
    //使用 类型判断 + 向下转型.
    if(persons[i]  instanceof  Student) {//判断person[i] 的运行类型是不是Student
        Student student = (Student)persons[i];//向下转型
        student.study();
        //((Student)persons[i]).study();
    } else if(persons[i] instanceof  Teacher) {
        Teacher teacher = (Teacher)persons[i];
        teacher.teach();
    } else if(persons[i] instanceof  Person){
        //System.out.println("你的类型有误, 请自己检查...");
    } else {
        System.out.println("你的类型有误, 请自己检查...");
    }

}

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

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;
    }
}

public class Worker extends Employee {
    public Worker(String name, double salary) {
        super(name, salary);
    }
    public void work() {
        System.out.println("普通员工 " + getName() + " is working");
    }
    public double getAnnual() { //因为普通员工没有其它收入,则直接调用父类方法
        return super.getAnnual();
    }
}

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");
    }
    //重写获取年薪方法
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

//主函数
public class PloyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom", 2500);
        Manager milan = new Manager("milan", 5000, 200000);
        PloyParameter ployParameter = new PloyParameter();
        ployParameter.showEmpAnnual(tom);
        ployParameter.showEmpAnnual(milan);

        ployParameter.testWork(tom);
        ployParameter.testWork(milan);

    }

    //showEmpAnnual(Employee e)
    //实现获取任何员工对象的年工资,并在main方法中调用该方法 [e.getAnnual()]
    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());//动态绑定机制.
    }
    //添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
    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的对比

  • == :既可以判断基本类型,又可以判断引用类型
  • == :如果判断基本类型,判断的是值是否相等
  • == :如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
  • equals :是Object类中的方法,只能判断引用类型
  • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等,比如Integer和String
Integer integer1 = new Integer(5);
Integer integer2 = new Integer(5);
System.out.println(integer1 == integer2);//false  比较的是地址,是不是同一个对象
System.out.println(integer1.equals(integer2));//true 比较的是内容
//String同

类中equals重写

public boolean equals(Person p){
    if(this == p){
        return true;
    }
    if(p instanceof Person){
        Person p1 = (Person)p;
        return this.name.equals(p.name) && this.gender == p.gender && this.age == p.age;
    }
    return false;
}

判断输出

char ch2 = 12;
char ch1 = 65;
int d = 12;
int dd = 'A';
System.out.println(d + ch2);//24
System.out.println(ch1);//A
System.out.println(dd);//65
System.out.println(12 == ch2);//true
System.out.println("hello" == new java.sql.Date());//编译报错,非同一类型且没有继承关系

hashCode方法

返回对象的哈希码值,为了提高哈希表的性能。

  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
  5. 集合中hashcode如果需要的话,也会重写
AA aa1 = new AA();
AA aa2 = new AA();
AA aa3 = aa1;
System.out.println("aa1 = " + aa1.hashCode());//aa1 = 356573597
System.out.println("aa2 = " + aa2.hashCode());//aa2 = 1735600054
System.out.println("aa3 = " + aa3.hashCode());//aa3 = 356573597

toString方法

返回该对象的字符串表示,通常,toString 方法会返回一个”以文本方式表示“此对象的字符串,结果应是一个简明但易于读懂的信息表达式,建议所有子类都重写此方法。

  • 默认返回:全类名+@+哈希值的十六进制【Object的toString方法】,子类往往重写toString方法,用于返回对象的属性信息
  • 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
  • 当直接输出一个对象时,toString方法会被默认的调用
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
}//Object的toString方法源码
//getClass().getName() 全类名,即包名+类名

//重写前
AA aa1 = new AA();
System.out.println(aa1.toString());//com.oop.objectdetail.AA@1540e19d

//重写后
Monster monster = new Monster("小妖怪", "巡山", 10.0);
System.out.println(monster.toString());//输出Monster{name='小妖怪', job='巡山', salary=10.0}
System.out.println(monster);//当直接输出一个对象时,toString方法会被默认的调用,输出同上

class Monster{
    private String name;
    private String job;
    private double salary;

    public Monster(String name, String job, double salary) {
        this.name = name;
        this.job = job;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", salary=" + salary +
                '}';
    }
}

finalize方法

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  • 当对象被回收时,系统自动调用该方法的finalize方法,子类可以重写该方法,做一些释放资源的操作
  • 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
  • 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动触发垃圾回收机制
  • 在实际开发中几乎不会运用finalize方法
Car car = new Car("大黄蜂");
car = null;//上面创建的Car对象就成了垃圾,垃圾回收器就会销毁对象
//在销毁对象前,会调用该对象的finalize方法,程序员就可以在这个方法中
//写自己的业务逻辑代码,比如释放资源等,如果程序员不重写,就会调用Object类的finalize方法,即默认处理
System.gc();//运行垃圾回收器,不一定会调用finalize方法
System.out.println("退出程序。");
//输出结果:退出程序。
//         销毁汽车大黄蜂
//         资源释放

class Car{
    private String name;

    public Car(String name) {
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("销毁汽车" + name);
        System.out.println("资源释放");
    }
}

断点调试(Debug)

  • 在断点调试过程中,是运行状态,是以对象的运行类型来执行的
  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看到各个变量的当前值,出错的话,调试到出错的代码行即显式错误,停下。进行分析从而找到Bug
  • 断点调试是程序员必须掌握的技能
  • 断点调试也能帮助我们查看Java底层源代码的执行过程,提高程序员的Java水平
  • 断点调试的快捷键
    • F7(跳入方法内) F8(跳过,逐行执行代码) Shift + F8(跳出方法) F9(resume,执行到下一个断点)
  • 小技巧:将光标放在某个变量上,可以看到最新的数据
  • 文件:idea debug如何进入jdk源码Setting—Build,Execution,Deployment—Debugger—Stepping

项目

public class MiniFund {
    private double balance;
    private double income;
    private double expense;
    //private String con_type;
    private String[][] bill = new String[20][3];
    private int count = 0;

    public MiniFund(double balance) {
        this.balance = balance;
        bill[count][0] = "收益入账";
        bill[count][1] = "+" + balance;
        bill[count][2] = balance + "";
    }

    public double consume(double con,String con_type){
        if(balance >= con && con > 0) {
            balance = balance - con;
            count++;
            bill[count][0] = con_type;
            bill[count][1] = "-" + con;
            bill[count][2] = balance + "";
        }else{
            System.out.println("您的余额不足或消费金额有误!");
        }
        return balance;
    }

    public double earn(double e){
        if(e > 0) {
            balance = balance + e;
            count++;
            bill[count][0] = "收益入账";
            bill[count][1] = "+" + e;
            bill[count][2] = balance + "";
        }else{
            System.out.println("您入账金额为负数!");
        }
        return balance;
    }

    public void showBill(){
        for (int i = 0; i <= count; i++) {
            System.out.println(bill[i][0] + "\t" + bill[i][1] + "\t" + "2021-10-25 17:02" + "\t" + "余额:" + bill[i][2]);
        }
    }
}

public class Use {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("---------------零钱通菜单--------------");
        System.out.println("            1.零钱通明细");
        System.out.println("            2.收益入账");
        System.out.println("            3.消费");
        System.out.println("            4.退出");
        int flag = 0;
        MiniFund my = new MiniFund(100);
        while(true){
            System.out.print("请选择(1-4)");
            flag = scanner.nextInt();
            if(flag == 1){
                System.out.println("---------------零钱通明细--------------");
                my.showBill();
            }else if(flag == 2){
                System.out.print("请输入您入账金额:");
                double ee = scanner.nextDouble();
                my.earn(ee);
            }else if(flag == 3){
                System.out.print("请输入您消费来源:");
                String c_type = scanner.next();
                System.out.print("请输入您消费金额:");
                double cc = scanner.nextDouble();
                my.consume(cc,c_type);
            }else if(flag == 4){
                System.out.println("您确定要退出吗?y/n");
                char ch = scanner.next().charAt(0);
                while(ch != 'y' && ch != 'n') {
                    System.out.println("您确定要退出吗?y/n");
                    ch = scanner.next().charAt(0);
                }
                if(ch == 'y') {
                    System.out.println("您已退出系统···");
                    break;
                }else{
                    continue;
                }
            }else{
                System.out.println("您输入的数字有误,已退出系统");
                break;
            }
        }
    }
}


//存在的问题
//1.账单用数组存储,有限数组---------老师直接字符串拼接
//2.账单输出格式有问题,上下对不齐-----------老师也没对齐
//3.时间的获取-------------新知识
//4.在判定选择的数据可以用switch

//编程思想:
//一块代码尽可能只做一件事情,耦合性比较小
//判断条件时,找出不正确的金额条件,然后给出提示退出,最好过关斩将,一关一关过,直到不通过或者通过
//把整个过程封装起来,客户只要调用就可以了
这篇关于Java中阶(1)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!