Java中级

IDAE介绍

IDEA,全称为IntelliJ IDEA,是一款由JetBrains公司开发的集成开发环境(IDE),主要用于Java开发,但也支持多种其他编程语言和框架,如Kotlin、Scala、Groovy、Android、Spring、Hibernate等。IDEA以其强大的功能和广泛的应用在开发者社区中享有很高的声誉,被许多企业和开源项目视为首选的开发工具。

文件介绍:

out存放编译后的.class文件 src存放源码文件.java

IDEA常用快捷键

删除当前行(修改后):Ctrl+D

复制当前行:Ctrl+A

补全代码:tab

注释Ctrl+/

导入该行所需要的类 :alt+enter

快速格式化 Ctrl+Alt+L

快速运行程序: Alt+r

快速生成构造器:右键,选择生成

查看类的继承关系:Ctrl+h

将光标放在一个方法上,输入Ctrl+b可以定位到方法

自动分配变量名: .var

作用:

1.区分相同名字的类

2.很好的管理类

3.控制访问范围

基本语法

package com.lyedu;

说明:package 关键词表示打包

com.lyedu;表示包名

本质分析

创建不同的文件夹/目录来保存类文件,如果想在一个类引用不同包的类,需要声明包名

包命名

规则和规范

规则:

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

规范:

一般是小写字母+小圆点

com.公司名.项目名.业务模块名

比如:com.lyedu.oa.model;

com.sina.crm.user//用户模块

com.sina.crm.order//订单模块

常用的包

java.lang*基本包,默认引入

java.util//util包,系统提供的工具包工具类,使用Scanner

java.net //网络包,网络开发

java.awt//是java的界面开发 GUI

java包的使用细节

如何引入包

语法Import包;

我们引入包的主要目的是要使用该包下的类

比如import java.util.Scanner;就是引入一个类Scanner.

import java.util.*//表示将java.util包下的所有类都导入

package com.use;

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 = {3, 5, 1, 5, 7, 9, 54};
        //比如对其进行排序,与传统方法自己编写相比,可以更方便的完成
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
    }
}

注意事项

1.package的作用是声明当前类所在包,需要放在类的最上面,一个类中最多只有一句package,

2.import指令 位置放在package的下面,在类定义面前,可以有多句且没有顺序要求

访问修饰符

基本介绍

1.public 公开级别,对外公开

2.protected: 受保护级别,对子类和同一个包的类公开

3.默认级别:无修饰符号,向同一个包的类公开

4.private:私有,只有类本身可以访问,对外不公开

使用注意事项

1.修饰符可以可以用来修饰类中的属性,成员方法以及类

2.只有默认和public才能修饰类,并且遵循上述访问权限的特点

3.成员方法的访问规则和属性完全一样

***面向对象编程三大特征***

封装

介绍

封装就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作.

优点

1.隐藏实现细节 方法<--调用(传入参数)

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

封装的实现步骤

1.对属性进行私有化

2.提供一个公共的set方法,用于对属性判断并赋值

public void setXxx(类型 参数名){

属性名=参数名

}

3.提供一个公共的get方法,用于获取属性的值

public XX getXxx(){

return xx;

}

package com.study.Encapsulation01;

public class Encapsulation01 {
    public static void main(String[] args) {
        //如果要使用快捷键运行,要先配置主类
        Person person = new Person();
        person.setName("小罗");
        person.setAge(20);
        person.setSalary(15000);
        System.out.println(person.info());
    }
}

class Person {
    public String name;
    private int age;
    private double salary;

    public void setName(String name) {
        //增加对数据的校验返回字符串长度的方法.length()
        if(name.length()>=2&&name.length()<=6){
            this.name = name;
        }else{
            System.out.println("名字长度不对,必须在2-6个字之间");
        }
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        if (age >= 18 && age <= 35) {
            this.age = age;
        }
        else {
            System.out.println("年龄必须在18-35");
            this.age = 18;
        }
    }

    public int getAge() {
        return age;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getSalary() {
        //可以增加对当前对象的权限判断
        return salary;
    }

    //下一个放方法返回信息属性
    public String info() {
        return "Name: " + name + ", Age: " + age + ", Salary: " + salary;
    }
}

封装和构造器

如果将上个程序中添加构造器,那么赋值时就不会用到set方法,也就是说封装失效了,那么我们该如何解决这个问题呢?其实很简单,只用在构造器中调用set方法就可以了.

Person person = new Person("小罗",200,15000);

public Person(String name, int age, double salary) {
        setName(name);
        setAge(age);
        setSalary(salary);
    }	

继承

为什么需要继承?

  1. 代码复用:通过继承,子类可以重用父类的代码,避免了重复编写相同的代码,提高了开发效率。
  2. 实现多态:继承是多态的基础,通过继承可以定义接口和实现类,实现运行时多态。
  3. 建立类之间的关系:继承使得类与类之间产生了层次关系,这种关系有助于理解系统的整体结构。
  4. 易于扩展和维护:当需要为系统增加新功能时,可以通过继承父类来创建新的子类,这样可以在不修改原有代码的基础上扩展系统的功能,同时保持系统的稳定性。

继承语法

class 子类 extends 父类{}

1.子类会自动拥有父类定义的属性和方法

2.父类有教超类 基类

3.子类又叫派生类

继承细节

1.子类继承了父类的所有的属性和方法,非私有的属性和方法可以在子类直接访问,但父类的私有属性和方法不能在子类中直接访问,要通过公共的方法去访问

2.子类一定会调用父类的构造器如果希望调用父类的某个构造器,则需要显示的调用一下:super(参数列表)

3.当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定父类的哪个构造器完成对父类的初始化工作,否则编译不通过

4.Java中,当子类构造器中没有显式地通过super关键字调用父类的其他构造器时,子类的构造器会自动调用父类的默认构造器(即无参构造器)。这种自动调用发生在子类构造器的第一行,即使这一行没有明确写出来。

5.super使用时必须放在构造器的第一行(super只能在构造器中使用),在

6.super(),this()都只能放在构造器的第一行,因此这两个不能共存

7.java所有类都是Object类的子类

8.父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类),从辈分高的构造器一路向下

9.子类最多只能直接继承一个父类,即java是单继承机制

10.继承不可乱用

典例分析:

创建一个C类的对象,先运行默认构造器,遇到this跳到对应的构造器即C类的有参构造,然后遇到了super(ha...)

跳到父类即B类的有参构造,有参构造前有一个没明确写出来的super(),跳到父类的默认构造器即A类的默认构造器,输出圆圈1,然后super()执行完毕输出圆圈2,此时super("haha")执行完毕返回输出圆圈3,此时this("hello")执行完毕输出圆圈4

***继承本质***

package com.study.extend_;

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        //1.首先看子类是否有该属性,如果有且可以访问就返回信息
//        2.如果子类没有就看父类有没有这个属性,如果有且可以访问就返回信息
        //3.如果父类没有就继续向上查找直到Object,报错...
        System.out.println(son.name);//大头儿子
        System.out.println(son.age);//39
        System.out.println(son.hobby);//旅游
    }
}

class GrandPa {
    String name = "大头爷爷";
    String hobby = "旅游";
}

class Father extends GrandPa {
    String name = "大头爸爸";
    int age = 39;
}

class Son extends Father {
    String name = "大头儿子";
}

super关键字

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

1.访问父类的属性和方法,但不能访问父类的private属性和方法 通过super.属性名/super.方法名

2.访问父类的构造器,通过super(参数列表), 好处是分工明确(父类属性由父类初始化,子类属性由子类初始化)

3.当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员必须通过super.如果没有重名,使用super this直接访问效果一样

4.super可以直接跳过本类找父类,遵循就近原则,层层向上,this.方法/属性和在本类直接使用时一样的,super是直接跳过本类的,但需要遵循访问权限

package com.study.super_;

public class B extends A{
    //public int n1=1022;
    public int n2 = 999;
    public void hi(){
        System.out.println(super.n1+" "+super.n2+" "+super.n3);//n4访问不了
    }
    public void cal(){
        System.out.println("B类的cal()");
    }
    public void sum(){
        //希望调用父类A的cal方法
        //此时因为子类没有cal方法,可以通过三种方式访问
        cal();//找cal方法时,先看本类有且可以调用就调用,如果没有找父类...
        //如果找到了但不能调用则报错
        //没找到则提示方法不存在,报错
        this.cal();//等价cal()
        super.cal();//跳过本类直接去找父类
        //演示属性的规则
        //n1和this.n1查找规则一样,先本类,本类无找父类
        //如果找到了但不能调用则报错
        //没找到则提示属性不存在,报错
        System.out.println(n1);//100
        System.out.println(this.n1);//100
        System.out.println(super.n2);//直接找父类200
    }


    public void ok(){
        super.test100();
        super.test200();
        super.test300();
//        super.test400();不可
    }
}

super和this的比较

方法的重写(override)

子类的一个方法和父类的方法返回类型名称参数一样,就称子类的方法覆盖了父类的方法.

注意事项

1.方法参数 方法名必须一样

2.子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类.例如:父类的返回类型是Object,子类是String,因为Object是所有子类的父类

3.子类方法不能缩小父类的访问权限:public>protected>默认>private,但可以扩大

方法重写和重载对比

综合案例

package com.study.override_;

public class Override01 {
    public static void main(String[] args) {
        //演示方法重写的情况
        Person person = new Person("刘宇",19);
        Student stu = new Student("刘宇",19,23130026,96.0);
        System.out.println(person.say());
        System.out.println(stu.say());

    }
}
package com.study.override_;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.setName(name);
        this.setAge(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 + " 今年" + age + "岁";
    }
}
package com.study.override_;

public class Student extends Person {
    private int id;
    private double score;

    public Student() {

    }

    public Student(String name,int age,int id, double score) {
        super(name,age);//这里会调用父类的构造器
        setId(id);
        setScore(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() + " 学号" + id + " 得分" + score;//利用super调用重写方法
    }
}

多态

方法或对象具有多种形态.建立在封装和继承的基础上

方法的多态

重写和重载

***对象的多态***

1.一个对象的编译类型和运行类型可以不一致

2.编译类型在定义对象时就确定了,不能改变

3.运行类型是可以变化的,可以通过getclass()来查看运行类型

4.编译类型看定义时=号的左边,运行类型看=的右边

快速入门

注意事项和细节讨论

多态的前提:两个类存在继承关系,多态向上转型

1.本质:父类的引用指向了子类的对象

2.语法 父类类型 引用名 = new 子类类型();

3.特点:编译类型看左边,运行类型看右边.

可以调用父类中的所有成员(需要遵守访问权限)

不能调用子类的特有成员

最终运行效果看子类的具体实现!

多态的向下转型

1.语法: 子类类型 引用名 =(子类类型)父类引用;

2.只能强转父类的引用,不能强转父类的对象

3.要求父类的引用必须指向的是当前目标类型的对象

4.当向下转型后可以调用子类类型的所有成员

属性不能重写

属性没有重写一说,属性的值看编译类型。题例输出10

instanceof

AA instanceof bb用于判断对象的运行类型是不是xx类型或xx类型的子类型

理解

向上转型(Upcasting)

定义:

向上转型是指将子类对象的引用赋值给父类类型的引用变量。这种转型是自动的,不需要显式地进行类型转换。编译类型代表只能调用的方法,运行类型代表从哪开始找方法

class Person{
    public void run(){
        System.out.println("person run");
    }
    public void eat(){
        System.out.println("person eat");
    }
}
class Student extends Person{
    public void run(){
        System.out.println("Student run");
    }
    public void study(){
        System.out.println("Student study");
    }
}
//向上转型,父类的引用指向子类的对象
Person p = new Student();
p.run();//student run
p.eat();//person eat
//编译类型确定p只能调用Person类的属性和方法,但查找从Student开始
Student p1 = (Student)p;
s.run();//student run
s.study();//Student study
s.eat();//person eat
作用:

增强代码的灵活性和可扩展性:通过父类类型的引用变量来引用子类类型的对象,可以在不修改原有代码的基础上,增加新的子类类型,实现代码的扩展。

强制规范类的结构和行为:通过向上转型,可以强制子类遵循父类的结构和行为,保证代码的一致性

实例

Animal animal1 = new Dog(); // Dog是Animal的子类,这里发生了向上转型

Animal animal2 = new Cat(); // Cat也是Animal的子类,这里也发生了向上转型

Animal animal = new Dog(); // 向上转型

Dog dog = (Dog) animal; // 向下转型,显式地指定了转换的类型

dog.bark(); // 调用Dog类特有的bark()方法

在这个例子中,animal1和animal2都是Animal类型的引用变量,但它们分别引用了Dog和Cat的对象。由于Dog和Cat都重写了Animal类的eat()方法,因此在调用eat()方法时,会根据实际引用的对象类型来调用相应的方法,实现了多态。

向下转型(Downcasting)

定义:

向下转型是指将父类类型的引用变量赋值给子类类型的引用变量。这种转型需要显式地进行类型转换,并且存在一定的风险,因为如果转换的类型不匹配,会抛出ClassCastException异常。

作用:

在某些情况下,我们需要访问子类特有的方法或属性,这时就需要通过向下转型将父类类型的引用变量转换为子类类型的引用变量。

示例:

Animal animal = new Dog(); // 向上转型

Dog dog = (Dog) animal; // 向下转型,显式地指定了转换的类型

dog.bark(); // 调用Dog类特有的bark()方法

在这个例子中,首先通过向上转型将Dog对象赋值给Animal类型的引用变量animal。然后,通过向下转型将animal转换为Dog类型的引用变量dog,从而可以调用Dog类特有的bark()方法。

注意事项:

在进行向下转型之前,最好使用instanceof运算符检查引用变量是否确实指向了目标子类的实例,以避免ClassCastException异常。

向下转型通常不是必须的,因为它破坏了多态的封装性,增加了代码的耦合度。在可能的情况下,应该尽量避免使用向下转型,而是通过多态的方式来实现代码的功能。

总结

向下转型(Downcasting)的主要目的是为了在向上转型(Upcasting)之后,能够重新获得对子类特定属性或方法的访问权限。然而,直接重新创建一个对象来访问这些属性或方法并不是总是一个可行的或最佳的选择,原因有以下几点:

避免重复创建对象:

如果对象已经存在,并且已经通过向上转型被赋值给了一个父类类型的引用变量,那么重新创建一个新的子类对象来访问其特有属性或方法将是多余的,并且可能导致不必要的资源消耗和性能问题。

保持状态一致性:

重新创建的对象将是一个全新的实例,它不会保留原有对象的状态(即其属性值和已执行的方法调用的结果)。而向下转型允许我们直接操作原始对象,保持其状态的一致性。

代码的可读性和维护性:

在复杂的系统中,直接操作原始对象(通过向下转型)可能比管理多个对象实例更容易理解和维护。特别是当这些对象之间存在复杂的依赖关系或状态时,重新创建对象可能会使代码变得更加混乱。

多态性的利用:

多态性是面向对象编程中的一个重要特性,它允许我们使用父类类型的引用来引用子类对象,并通过这个引用来调用子类重写的方法。在某些情况下,我们可能希望先以父类类型的视角来操作对象(例如,将对象放入一个父类类型的集合中),然后再根据需要将其转换回子类类型来访问其特有功能。这种灵活性是多态性带来的好处之一。

当然,直接重新创建一个对象来访问子类特定的属性或方法在某些情况下也是可行的,特别是当原始对象不再需要或无法访问时。然而,在大多数情况下,我们应该优先考虑使用多态性和向下转型来保持代码的灵活性和可维护性。

*****JAVA的动态绑定机制*****

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

2.当调用对象属性时,没有动态绑定机制,哪里声明哪里调用

package com.study.Poly_.dynamic_;

public class Test {
    public static void main(String[] args) {
        //a的编译类型是A,运行类型为B
        A a = new B();
        System.out.println(a.sum());//40->30注销子类的sum
        System.out.println(a.sum1());//30->20注销子类的sum1
    }
}
package com.study.Poly_.dynamic_;

public class A {
    public int i = 10;
//动态绑定机制 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
    //注销父类的sum方法时,会调用子类的sum方法,sum中有个getI方法
// 但由于动态绑定机制,故调用的是运行类型的方法
    public int sum() {
        return getI() + 10;
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i;
    }
}
package com.study.Poly_.dynamic_;

public 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;
//    }
}
/*分析:
1.将子类的sum方法注销后,再去进行测试,子类没有sum根据继承机制,开始访问父类的sum方法,
找到并成功访问,进入方法,遇到getI方法,由于Java方法的动态绑定机制,他会调用运行类型的方法,
即子类的方法,进入子类的getI方法,遇到属性i,由于属性不存在动态绑定,在哪里声明就在那里使用,
因此会返回子类的i,即20到父类的sum方法,+10故输出30
2.将子类的sum1方法注销后,再去进行测试,子类没有sum1根据继承机制,开始访问父类的sum1方法,
找到并成功访问,进入方法,由于属性不存在动态绑定,在哪里声明就在那里使用,因此会返回父类的i
,i+10返回,故输出20.
*/

分析:

1.将子类的sum方法注销后,再去进行测试,子类没有sum根据继承机制,开始访问父类的sum方法,

找到并成功访问,进入方法,遇到getI方法,由于Java方法的动态绑定机制,他会调用运行类型的方法,

即子类的方法,进入子类的getI方法,遇到属性i,由于属性不存在动态绑定,在哪里声明就在那里使用,

因此会返回子类的i,即20到父类的sum方法,+10故输出30

2.将子类的sum1方法注销后,再去进行测试,子类没有sum1根据继承机制,开始访问父类的sum1方法,

找到并成功访问,进入方法,由于属性不存在动态绑定,在哪里声明就在那里使用,因此会返回父类的i

,i+10返回,故输出20.

多态数组

//创建两个Student对象和2个Teacher对象
        Person[] persons = new Person[5];
//向上转型
        persons[0] = new Person(20,"jack");
        persons[1] = new Student("jack",18,100);
        persons[2] = new Student("iverson",19,30.1);
        persons[3] = new Teacher(30,"yudian",15000);
        persons[4] = new Teacher(59,"junan",25000);
        //循环遍历多态数组调用say
        for (int i = 0; i < persons.length; i++) {
            //编译类型都是person,运行类型根据实际情况来判断
            System.out.println(persons[i].say());
        }

多态参数

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

例子:

package com.study.Poly_.Array_.ex;

public class TestEmployee {
    public static void main(String[] args) {
        Employee[] employee = new Employee[5];
        Worker tom = new Worker("tom",8000);
        manager milan=new manager("milan",10000,50000);
        TestEmployee a=new TestEmployee();
        a.testWork(tom);
        a.showEmpAnnual(tom);
        a.testWork(milan);
        a.showEmpAnnual(milan);
    }

    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());//动态绑定机制
    }

    public void testWork(Employee e) {
        if(e instanceof Worker){//利用instanceof来判断e是否是Worker类型或者是其子类型
            ((Worker) e).work();//向下转型
        }else if(e instanceof manager){
            ((manager)e).manage();//向下转型
        }else{
            System.out.println("nothing");
        }
    }
}
package com.study.Poly_.Array_.ex;

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.setName(name);
        this.setSalary(salary);
    }


    public String getAnnual() {
        return "name" + name + "的年薪为" + salary * 12;
    }

    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.study.Poly_.Array_.ex;

public class Worker extends Employee {

    public Worker(String name, double salary) {
        super(name, salary);
    }

    public void work() {
        System.out.println("工作者" + getName());
    }

    public String getAnnual() {
        return "工人" + getName() + "的年薪为" + getSalary() * 12;
    }
}
package com.study.Poly_.Array_.ex;

public class manager extends Employee {
    private double bonus;

    public manager(String name, double salary, double bonus) {
        super(name, salary);
        this.setBonus(bonus);
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public void manage() {
        System.out.println("管理者" + getName());
    }

    public String getAnnual() {
        return "管理者" + getName() + "的年薪为" + (getSalary() * 12 + bonus);
    }
}

Object类讲解(部分)

equals方法

equals和==比较

1.==是一个比较运算符,既可以判断基本类型又可以判断引用类型

2.基本类型:判断值是否相等

3.引用类型:判断地址是否相等

4.equals只能判断引用类型

public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Doctor) {
            Doctor a = (Doctor) obj;
            return this.name.equals(a.name) && this.sal == a.sal &&
                    this.age == a.age && this.job.equals(a.job) && this.gender == a.gender;
        } else {
            return false;
        }
    }

IDEA怎么看JDK源码?

ctrl+左键,光标点击

5.equals方法默认判断地址是否相等,他的子类通常会重写该方法,用于判断内容是否相等,如String Integer,可通过看源码方法了解

hashCode方法hashCode()

1.提高具有哈希结构的容器的效率

2.两个引用,如果指向的是同一个对象,则哈希值肯定是一样的,指向的是不同对象,则哈希值是不一样的

3.哈希值主要根据地址号来的,但不能完全将哈希值等价于地址(Java的虚拟机机制)

4.哈希方法也可以重写

toString方法

默认返回:类的全类名(包名+类名)+@+哈希值的16机制

子类往往重写该方法,用于返回对象的属性信息

public String toString(){
    return getClass().getName()+"@"+Integer.toHexString(hashCode());
}

一般会进行重写用于把对象的属性拼接输出

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

当直接输出一个对象时,会默认调用toString 方法

System.out.println(monster);等价System.out.println(monster.toString());

finalize方法

1.当对象被回收时,系统会自动调用该对象的finalize方法.子类可以重写该方法,做一些内存的释放

2.什么时候被回收:当某个对象没有任何引用时,则jvm救人位这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁对象前,会先调用finalize方法.程序员就可以通过重写finalize方法实现实现自己的业务逻辑,如果不重写就会调用Object类的finalize,即默认处理

3.垃圾回收机制的调用是由系统类决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制

实际开发中,几乎用不动finalize方法,更多的是为了应付面试

断点调试

断点调试说明

1.断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下,进而分析从而找到这个bug

2.断点调试是程序员必须掌握的技能

3.断点调试也能够帮助我们查看Java底层源代码的执行过程,提高程序员Java水平

快捷键

F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下个断点)

F7:跳入方法内

F8:逐行执行代码

shift+F8:跳出方法

断点可以在Debug过程中动态的下断点

如何执行到下个断点F9 按resume

项目-零钱通

化繁为简:先分布实现基础功能

进行改进:1.完善退出

2.完善输入合法

3.面向过程->面向对象oop

package com.items.SmallChange;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangesys {
    //化繁为简
    //1.先显示菜单,并可以选择菜单,给出对应提示
    //2.完成零钱通明细
    //三种思路:(1)可以使用数组来保存收益入账和消费支出(2)可以使用对象(3)简单的话可以使用String拼接
    //3.完成收益入账
    //4.完成消费
    //5.设计退出确认

    public static void main(String[] args) {
        //1.完成菜单
        boolean loop = true;
        Scanner myScanner = new Scanner(System.in);
        String key = " ";

        //2.完成零钱通明细
        //三种思路:使用String拼接,输出收益明细
        String details = "------------零钱通明细----------";

        //        3.完成收益入账
        double money = 0.0;
        double balance = 0.0;
        Date date = null;//date是java.util.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.print("请选择(1-4): ");
            key = myScanner.next();
            switch (key) {
                case "1":
                    System.out.println(details);
                    break;
                case "2":
                    System.out.println("收益入账金额: ");
                    money = myScanner.nextDouble();
                    //找出不合法的金额给出提示即可
                    //编程思想:正难则反
                    if(money<=0){
                        System.out.println("收益图纸金额需要 大于0");
                        break;
                    }
                    balance += money;
                    date = new Date();//获取当前日期
//                    System.out.println(sdf.format(date));2
                    details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t余额剩余:" + balance;
                    //+=是为了存储上次循环的值,所以也要用换行拼接
                    break;
                case "3":
                    System.out.println("消费金额");
                    money = myScanner.nextDouble();
                    if(money<=0||money>balance){
                        System.out.println("您的消费金额必须在0-"+balance+"之间");
                                break;
                    }
                    System.out.println("消费说明:");
                    note = myScanner.next();
                    balance -= money;
                    //拼接信息到消费说明
                    date = new Date();//获取当前日期
                    details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额剩余 " + balance;
                    break;	
                case "4":
//                    System.out.println("4 退   出");
                    //(1)定义一个变量choice接收用户的输入
                    //(2)使用一个while循环来处理接收得到是y/n
                    String choice="";
                    while(true){//一段代码完成一个小功能,要求用户必须输入y/n否则一致循环
                        System.out.println("你确定要退出循环吗?y/n");
                        choice = myScanner.next();
                        if("y".equals(choice)||"n".equals(choice)){
                            break;
                        }
                    }
                    //用户退出后
                    if(choice.equals("y")){//判断是否输入的是y
                        loop=false;
                    }//是n不处理
                    break;
                default:
                    System.out.println("菜单选择有误,请重新选择");
            }
        } while (loop);
        System.out.println("退出了零钱通...");
    }
}

通过创建一个新类来调用他的相关方法来实现功能

package com.items.SmallChange.oop;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;//使用oop面向对象编程
//将各个功能封装成方法
public class SmallChangeSysoop{
    boolean loop = true;
    Scanner myScanner = new Scanner(System.in);
    String key = " ";

    String details = "------------零钱通明细----------";

    double money = 0.0;
    double balance = 0.0;
    Date date = null;//date是java.util.Date类型,表示日期
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//用于日期格式化

    String note = "";

    //显示菜单,并可以选择
    public void mainMenu(){
        do {
            System.out.println("\n===========零钱通菜单===========");
            System.out.println("\t\t\t1 零钱通明细");
            System.out.println("\t\t\t2 收益入账");
            System.out.println("\t\t\t3 消费");
            System.out.println("\t\t\t4 退       出");

            System.out.print("请选择(1-4): ");
            key = myScanner.next();
            //封装成方法
            switch (key) {
                case "1":
                    this.detail();
                    break;
                case "2":
                    this.income();
                    break;
                case "3":
                    this.outcome();
                    break;
                case "4":
                    this.exit();
                    break;
                default:
                    System.out.println("菜单选择有误,请重新选择");
            }
        } while (loop);
        System.out.println("退出了零钱通...");
    }
    //完成零钱通明细
    public void detail(){
        System.out.println(details);
    }
    //完成收益明细
    public void income(){
        System.out.println("收益入账金额: ");
        money = myScanner.nextDouble();
        //找出不合法的金额给出提示即可
        //编程思想:正难则反
        if(money<=0){
            System.out.println("收益图纸金额需要 大于0");
            return;//退出方法
        }
        balance += money;
        date = new Date();//获取当前日期
//                    System.out.println(sdf.format(date));2
        details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t余额剩余:" + balance;
        //+=是为了存储上次循环的值,所以也要用换行拼接

    }
    //完成消费
    public void outcome(){
        System.out.println("消费金额");
        money = myScanner.nextDouble();
        if(money<=0||money>balance){
            System.out.println("您的消费金额必须在0-"+balance+"之间");
            return;
        }
        System.out.println("消费说明:");
        note = myScanner.next();
        balance -= money;
        //拼接信息到消费说明
        date = new Date();//获取当前日期
        details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额剩余 " + balance;
    }
    //退出
    public void exit(){
        String choice="";
        while(true){//一段代码完成一个小功能,要求用户必须输入y/n否则一致循环
            System.out.println("你确定要退出循环吗?y/n");
            choice = myScanner.next();
            if("y".equals(choice)||"n".equals(choice)){
                break;
            }
        }
        if(choice.equals("y")){//判断是否输入的是y
            loop=false;
        }//是n不处理

    }
}

创建对象调用

package com.items.SmallChange.oop;

//这里我们是直接调用SmallChangeSysoop对象:显示主菜单
public class SmallChangeSysApp {
    public static void main(String[] args) {
        new SmallChangeSysoop().mainMenu();
    }
}

项目-房屋出租系统

实现功能三部曲:明确完成功能->思路分析->代码实现

设计框架,实现特定功能

具体见文件test01.src.com.items.houseren

主要用一个包来作为主菜单,一个类来调用实现功能,一个类通过被调用实现业务功能,一个工具包

package com.items.houserent.utils;


/**
 * 工具类的作用:
 * 处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
 */

import java.util.*;

/**


 */
public class Utility {
    //静态属性。。。
    private static Scanner scanner = new Scanner(System.in);


    /**
     * 功能:读取键盘输入的一个菜单选项,值:1——5的范围
     * @return 1——5
     */
    public static char readMenuSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1, false);//包含一个字符的字符串
            c = str.charAt(0);//将字符串转换成字符char类型
            if (c != '1' && c != '2' &&
                    c != '3' && c != '4' && c != '5') {
                System.out.print("选择错误,请重新输入:");
            } else break;
        }
        return c;
    }

    /**
     * 功能:读取键盘输入的一个字符
     * @return 一个字符
     */
    public static char readChar() {
        String str = readKeyBoard(1, false);//就是一个字符
        return str.charAt(0);
    }

    /**
     * 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符
     * @param defaultValue 指定的默认值
     * @return 默认值或输入的字符
     */

    public static char readChar(char defaultValue) {
        String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符
        return (str.length() == 0) ? defaultValue : str.charAt(0);
    }

    /**
     * 功能:读取键盘输入的整型,长度小于2位
     * @return 整数
     */
    public static int readInt() {
        int n;
        for (; ; ) {
            String str = readKeyBoard(10, false);//一个整数,长度<=10位
            try {
                n = Integer.parseInt(str);//将字符串转换成整数
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return n;
    }

    /**
     * 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数
     * @param defaultValue 指定的默认值
     * @return 整数或默认值
     */
    public static int readInt(int defaultValue) {
        int n;
        for (; ; ) {
            String str = readKeyBoard(10, true);
            if (str.equals("")) {
                return defaultValue;
            }

            //异常处理...
            try {
                n = Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return n;
    }

    /**
     * 功能:读取键盘输入的指定长度的字符串
     * @param limit 限制的长度
     * @return 指定长度的字符串
     */

    public static String readString(int limit) {
        return readKeyBoard(limit, false);
    }

    /**
     * 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串
     * @param limit 限制的长度
     * @param defaultValue 指定的默认值
     * @return 指定长度的字符串
     */

    public static String readString(int limit, String defaultValue) {
        String str = readKeyBoard(limit, true);
        return str.equals("") ? defaultValue : str;
    }


    /**
     * 功能:读取键盘输入的确认选项,Y或N
     * 将小的功能,封装到一个方法中.
     * @return Y或N
     */
    public static char readConfirmSelection() {
        System.out.println("请输入你的选择(Y/N): 请小心选择");
        char c;
        for (; ; ) {//无限循环
            //在这里,将接受到字符,转成了大写字母
            //y => Y n=>N
            String str = readKeyBoard(1, false).toUpperCase();
            c = str.charAt(0);
            if (c == 'Y' || c == 'N') {
                break;
            } else {
                System.out.print("选择错误,请重新输入:");
            }
        }
        return c;
    }

    /**
     * 功能: 读取一个字符串
     * @param limit 读取的长度
     * @param blankReturn 如果为true ,表示 可以读空字符串。
     * 					  如果为false表示 不能读空字符串。
     *
     *	如果输入为空,或者输入大于limit的长度,就会提示重新输入。
     * @return
     */
    private static String readKeyBoard(int limit, boolean blankReturn) {

        //定义了字符串
        String line = "";

        //scanner.hasNextLine() 判断有没有下一行
        while (scanner.hasNextLine()) {
            line = scanner.nextLine();//读取这一行

            //如果line.length=0, 即用户没有输入任何内容,直接回车
            if (line.length() == 0) {
                if (blankReturn) return line;//如果blankReturn=true,可以返回空串
                else continue; //如果blankReturn=false,不接受空串,必须输入内容
            }

            //如果用户输入的内容大于了 limit,就提示重写输入
            //如果用户如的内容 >0 <= limit ,我就接受
            if (line.length() < 1 || line.length() > limit) {
                System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
                continue;
            }
            break;
        }

        return line;
    }
}

package com.items.houserent;

import com.items.houserent.view.HouseView;

//该类作为程序的入口,选择功能
public class HouseRentApp {
    public static void main(String[] args) {
        //创建对象,作为程序的路口
        new HouseView().mainMenu();
        System.out.println("=====你退出了房屋出租系统=====");
    }
}
package com.items.houserent.domain;

//此类用于初始化对象信息,返回对象输出形式
public class House {
    //编号 房主 电话 地址 月租 状态
    private int id;
    private String name;
    private String phone;
    private String address;
    private int ren;
    private String state;

    public House(int id, String name, String phone, String address, int ren, String state) {
        this.setId(id);
        this.setName(name);
        this.setPhone(phone);
        this.setAddress(address);
        this.setRen(ren);
        this.setState(state);
    }

    @Override
    public String toString() {
        return id +
                "\t\t" + name +
                "\t" + phone +
                "\t" + address +
                "\t" + ren +
                "\t" + state;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getRen() {
        return ren;
    }

    public void setRen(int ren) {
        this.ren = ren;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}
package com.items.houserent.service;

import com.items.houserent.domain.House;

public class HouseService {
    private House[] houses;
    private int houseNums = 1;//记录当前有多少个房屋信息
    private int idcount = 1;//记录id当前增长到哪

    public HouseService(int size) {
        houses = new House[size];//当创建类对象指定数组大小
        //测试列表信息,初始化一个House对象
        houses[0] = new House(1, "jack", "1123", "江夏区", 2000, "未出租");
        houses[1] = new House(2, "罗", "1133", "监利", 800, "已出租");
    }

    //list方法,返回houses
    public House[] list() {
        return houses;
    }

    //add添加新对象 返回boolean值,以便后续的条件判断
    public boolean add(House newhouse) {
        //判断是否还可以继续添加
        if (houseNums == houses.length) {
            System.out.println("已满,不可再加");
            return false;
        }
        houses[houseNums++] = newhouse;//后++,先赋值后自增,每新增一个对象,记录房屋信息就+1
        //需要设计一个id自增长机制
        newhouse.setId(++idcount);
        return true;
    }

    //创建一个find方法,返回boolean,用于查找并输出相应id信息
    public House find(int findId) {
        for (int i = 0; i < houseNums; i++) {
            if (houses[i].getId() == findId) {
                return houses[i];
            }
        }
        return null;
    }

    public boolean del(int delId)
        {
            //要找到房屋信息所对应的下标
            //由于房屋对应信息会变话,下标和房屋的编号不是一回事
            int index = -1;
            for (int i = 0; i < houseNums; i++) {
                if (delId == houses[i].getId()) {//要删除的访问id,是数组下标的i元素
                    index = i;
                }
            }
            if (index == -1) {//说明delId在数组中不存在
                return false;
            }
            //如果找到如何删除?
            for (int i = index; i < houseNums - 1; i++) {
                houses[i] = houses[i + 1];
            }
            houses[--houseNums] = null;//删除一个,他的记录房数-1,前--,先-在赋值
            return true;
        }
}
package com.items.houserent.view;

import com.items.houserent.domain.House;
import com.items.houserent.service.HouseService;
import com.items.houserent.utils.Utility;

/*
1.显示界面
2.接收用户输入
3.调用HouseService完成对房屋信息的各种操作
 */
public class HouseView {
    private boolean loop = true;
    private char key = ' ';
    private HouseService houseservice = new HouseService(10);//设置数组大小为10
    
    //根据id修改房屋信息
    public void update() {
        System.out.println("--------修改房屋信息--------");
        System.out.print("请选择待修改房屋编号(-1退出):");
        int updateId = Utility.readInt(1);
        if (updateId == -1) {
            System.out.println("--------放弃修改房屋信息--------");
            return;
        }
        //根据输入的updateId查找对象
        //返回是引用类型,顾后面进行修改可以修改数组中的元素
        House house = houseservice.find(updateId);
        if (house == null) {
            System.out.println("不好意思,你输入的编号不存在");
            return;
        }
        System.out.print("姓名(" + house.getName() + "): ");
        String name = Utility.readString(8, "");//回车米默认就是""
        if (!"".equals(name)) {//修改
            house.setName(name);
        }
        System.out.print("电话(" + house.getPhone() + "): ");
        String phone = Utility.readString(12);
        house.setPhone(phone);
        System.out.print("地址(" + house.getAddress() + "): ");
        String address = Utility.readString(16);
        house.setAddress(address);
        System.out.print("租金(" + house.getRen() + "): ");
        int rent = Utility.readInt();
        house.setRen(rent);
        System.out.print("状态(" + house.getState() + "): ");
        String state = Utility.readString(3);
        house.setState(state);
        System.out.println("修改信息成功!");
    }

    //编写findHouse完成查找功能
    public void findHouse() {
        System.out.println("--------查找房屋--------");
        System.out.print("请选择待查找房屋编号(-1退出):");
        int findId = Utility.readInt(1);
        if (findId == -1) {
            System.out.println("--------放弃查找--------");
            return;
        }
        if (houseservice.find(findId) != null) {
            System.out.println("已找到,信息如下");
            System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(已出租/未出租)");
            System.out.println(houseservice.find(findId));
        } else {
            System.out.println("不好意思,你输入的编号不存在");
        }
    }

    //完成退出确认
    public void exit() {
        //使用Utility提供的方法,完成确认
        char c = Utility.readConfirmSelection();
        if (c == 'Y') {
            loop = false;
        }
    }

    //编写delHouse接收用户输入的ID
    public void delHouse() {
        System.out.println("--------删除房屋--------");
        System.out.print("请选择待删除房屋编号(-1退出):");
        int delId = Utility.readInt(1);
        if (delId == -1) {
            System.out.println("--------放弃删除--------");
            return;
        }
        //该方法有循环判断逻辑,不输入y/n出不来
        char choice = Utility.readConfirmSelection();
        if (choice == 'Y') {//真的删除,调用del方法
            if (houseservice.del(delId)) {
                System.out.println("========删除房屋信息成功========");
            } else {
                System.out.println("========房屋编号不存在,删除失败========");
            }
        } else {
            System.out.println("========取消删除房屋信息========");
        }
    }

    //编写addHouse()接收输入,创建House对象,调用add方法
    public void addHouse() {
        System.out.println("=========添加房屋========");
        System.out.print("姓名: ");
        String name = Utility.readString(8);
        System.out.print("电话: ");
        String phone = Utility.readString(12);
        System.out.print("地址: ");
        String address = Utility.readString(16);
        System.out.print("月租: ");
        int rent = Utility.readInt();
        System.out.print("状态: ");
        String state = Utility.readString(3);
        //创建一个新的House对象,将新对象传入add方法(实际实现/业务上实现添加功能的方法)注意id系统分配
        House newHouse = new House(0, name, phone, address, rent, state);
        if (houseservice.add(newHouse)) {
            System.out.println("========添加房屋成功========");
        } else {
            System.out.println("========添加房屋失败========");
        }
    }

    //编写listHouse()显示房屋列表
    public void listHouse() {
        System.out.println("\n========房屋列表========");
        System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(已出租/未出租)");
        House[] houses = houseservice.list();//得到所有房屋信息,houses数组来接收
        for (int i = 0; i < houses.length; i++) {
            if (houses[i] != null) {
                System.out.println(houses[i]);
            }
        }
        System.out.println("========房屋列表显示完毕========\n");
    }

    public void mainMenu() {
        do {
            System.out.println("========房屋出租系统========");
            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("\t\t\t5 房屋列表");
            System.out.println("\t\t\t6 退     出");
            System.out.println("请选择(1-6)");
            key = Utility.readChar();
            switch (key) {
                case '1':
                    addHouse();
                    break;
                case '2':
                    findHouse();
                    break;
                case '3':
                    delHouse();
                    break;
                case '4':
                    update();
                    break;
                case '5':
                    listHouse();
                    break;
                case '6':
                    exit();
                    break;
            }
        } while (loop);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值