一 IDEA使用
1 IDEA使用技巧和经验
(1)设置字体
菜单 file -> settings
(2)字符编码设置
2 IDEA常用快捷键
(1)删除当前行,默认ctrl + y,可以改成ctrl + d;
(2)复制当前行到下一行,ctrl +alt + 向下光标;
(3)补全代码:alt + /;
(4)添加注释和取消注释:ctrl + /;
(5)导入该行需要的类,先配置 auto import ,然后使用alt + enter即可;
(6)快速格式化代码 ctrl + alt + L;
(7)快速运行程序,自己定义 alt + R。
(8)生成构造方法等 alt + insert; [提高开发效率]
(9)查看一个类的层次关系 ctrl + H;
(10)将光标放在一个方法上,输入ctrl + B,可以选择定位到哪个类的方法;
(11)自动的分配变量名,通过 在后面 .var;
3 模板/自定义模板
file -> setting -> editor -> Live templates ->查看有哪些模板快捷键/可以自己增加模板
二 包
1 包的三大作用
(1)区分相同名字的类;
(2)当类很多时,可以很好的管理类;
(3)控制访问范围。
2 包的基本语法
package com.lyxlearn;
说明:
(1)package 关键字:表示打包;
(2)com.lyxlearn:表示包名。
3 包的本质分析(原理)
实际上就是创建不同的文件夹/目录来保存类文件。
4 包的命名
(1)命名规则
只能包含数字、字母、下划线、小圆点. ,但不能用数字开头,不能是关键字或保留字。
(2)命名规范
一般是小写字母 + 小圆点;一般是com.公司名.项目名.业务模块名。
5 常用的包
(1)java.lang.* //lang包是基本包,默认引入,不需要再引入;
(2)java.util.* //util包,是系统提供的工具包,工具类,例如使用Scanner;
(3)java.net.* //网络包,网络开发;
(4)java.awt.* //是做java的界面开发,GUI。
6 注意事项和使用细节
(1)package
作用是声明当前类所在的包,需要在类的最上面,一个类中最多只有一句package;
(2)import指令
位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
三 访问修饰符
1 基本介绍
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)。
(1)公开级别:用 public 修饰,对外公开;
(2)受保护级别:用 protected 修饰,对子类和同一个包中的类公开;
(3)默认级别:没有修饰符号,向同一个包的类公开;
(4)私有级别:用 private 修饰,只有类本身可以访问,不对外公开。
2 四种访问修饰符的访问范围
3 使用的注意事项
(1)修饰符可以用来修饰类中的属性,成员方法以及类;
(2)只有默认和public才能修饰类,并遵循上述访问权限的特点;
(3)讲完子类再说;
(4)成员方法的访问规则和属性完全一样。
四 OOP三大特征(封装,继承和多态)🚩
1 封装(encapsulation)
(1)介绍
封装就是把抽象出来的数据【属性】和对数据的操作【方法】封装在一起,数据被保护再内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作。
(2)理解和好处
① 隐藏实现细节;
② 可以对数据进行验证,保证安全合理。
(3)实现步骤(三步)
① 将属性进行私有化private 【不能直接修改属性】;
② 提供一个公共的(public)set方法,用于对属性判断并赋值;
public void setXxx(类型 参数名){ //Xxx 表示某个属性
//加入数据验证的业务逻辑
属性 = 参数名;
}
③ 提供一个公共的(public)get方法,用于获取属性的值;
public 数据类型 getXxx(){ //权限判断,Xxx 某个属性
return xx;
}
便捷方法:
摁alt + insert 键 弹出框,选择Getter and Setter即可。
(4)封装和构造器合用
为了避免构造器破环私有访问属性的隐私,可以将set方法在构造器内调用即可。
2 继承
(1)基本介绍和示意图
继承可以解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类种定义这些相同的属性和方法,所有的子类不需要重写定义这些属性和方法,只需要通过extends来声明继承父类即可。
(2)基本语法
class 子类 extends 父类{
}
① 子类就会自动拥有父类定义的属性和方法;
② 父类又叫超类,基类;
③ 子类又叫派生类。
(3)细节问题
① 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法区访问;
② 子类必须调用父类的构造器,完成父类的初始化;
③ 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器种用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过;
④ 如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表);(若是调用父类的无参构造器,可以写super();或者说明都不写。)
⑤ super在使用时,必须放在构造器的第一行;(super只能在构造器中使用)
⑥ super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器;
⑦ java所有类都是Object类的子类,Object是所有类的基类;
⑧ 父类构造器的调用不限于直接父类,将一直往上追溯知道Object类(顶级父类),从上向下依次调用;
⑨ 子类最多只能继承一个父类(指直接继承),即java中的单继承机制;
思考:如何让A类继承B类和C类?
答:可以让A类继承B类,B类继承C类,与题目同样的效果;
⑩ 不能滥用继承,子类和父类之间必须满足is - a 的逻辑关系;
(4)继承的本质分析🚩
访问时:
① 首先看子类是否有该属性;
② 如果子类有这个属性,并且可以访问,则返回信息;
③ 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息…);
④ 如果父类没有就按照 ③ 的规则,继续找上级父类,直到Object…。
3 多态
(1)基本介绍
方法和对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础上的。
(2)具体表现
① 方法的多态
② 对象的多态🚩
a 一个对象的编译类型和运行类型可以不一致;
例如:Animal animal = new Dog(); //animal 编译类型是Animal,运行类型Dog(其中Animal是Dog的父类)
b 编译类型在定义对象时,就确定了,不能改变;
c 运行类型是可以改变的;
例如:animal = new Cat();
d 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
(3)多态注意事项和使用细节
多态的前提是:两个对象(类)存在继承关系;
① 多态的向上转型
a 本质:父类的引用指向(接收)了子类的对象;
b 语法:父类类型 引用名 = new 子类类型();
c 特点如下:
(a)可以调用父类中的所有成员(需要遵循访问权限);
(b)不能调用子类的特有的成员,因为在编译阶段,能够调用哪些成员,是由编译类型来决定的;
(c)最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则和前面讲的方法调用规则一致。
② 多态的向下转型
a 语法:子类类型 引用名 = (子类类型)父类引用;
b 只能强转父类的引用,不能强转父类的对象;
c 要求父类的引用必须指向的是当前目标类型(即等号前的子类类型)的对象;
d 当向下转型后,可以调用子类类型中所有的成员。
③ 属性没有重写之说,属性的值看编译类型;
④ instanceof 比较操作符:用于判断对象xx的运行类型是否为XX类型或者XX类型的子类型。例如 xx instanceof XX;
(4)java的动态绑定机制🚩🚩
① 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定;
② 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
public class DynamicBinding{
public static void main(String[] args){
A a = new B();//a的编译类型是 A,运行类型是B
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class A{ //父类
public int i = 10;
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 sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
此时结果为40,30。
public class DynamicBinding{
public static void main(String[] args){
A a = new B();//a的编译类型是 A,运行类型是B
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class A{ //父类
public int i = 10;
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 sum1(){
//return i + 10;
//}
public int getI(){
return i;
}
}
此时结果为30,20。
对于30,因为子类中没有sum()方法,所以会去父类中调用sum()方法,调用方法中的return有getI()方法,因为当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,此时对象的运行类型是B,所以调用B类中的getI()方法,返回20,加上10,得到30。
对于20,因为子类中没有sum1()方法,所以会去父类中调用sum1()方法,调用方法中有变量i,因为当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用,所以使用A类中声明的i=10,加上10,等于20。
(5)多态的应用
① 多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
实例:现有一个继承结构如下,要求创建1个Person对象,2个Student对象和2个Teacher对象,统一放在数组中,并调用say方法。
//Person类
public class Person {
private String name = "li";
private int age = 23;
public Person() {
}
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;
}
}
//Student类
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;
}
@Override
public String say() {
return super.say() + "\t" + score;
}
public void study(){
System.out.println("学生" + getName() + "正在听课...");
}
}
//Teacher类
public class Teacher extends Person{
private double sal;
public Teacher(String name, int age, double sal) {
super(name, age);
this.sal = sal;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
@Override
public String say() {
return super.say() + "\t" + sal;
}
public void teach(){
System.out.println("老师" + getName() + "正在讲课...");
}
}
//主类
public class PolyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("hack",30);
persons[1] = new Student("jack1",31,60);
persons[2] = new Student("jack2",35,80);
persons[3] = new Teacher("mark1",50,9000);
persons[4] = new Teacher("mark2",55,10000);
for (int i = 1; i < persons.length; i++) {
System.out.println(persons[i].say());
}
}
}
如何调用子类特有的方法,比如Teacher有一个teach,Student有一个study,怎么调用?
使用向下转型即可。
//在主类for循环内加如下代码即可
if (persons[i] instanceof Student) {
Student student = (Student)persons[i];
student.study();
//也可以写成 ((Student)persons[i]).study();
}else if (persons[i] instanceof Teacher) {
((Teacher) persons[i]).teach();
}
② 多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型。
a 定义员工类Employee,包含姓名和月工资【private】,以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法;
b 测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法;
c 测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法。
//Employee类
public class Employee {
private String name;
private double sal;
public Employee(String name, double sal) {
this.name = name;
this.sal = sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public String getAnnual(){
return "姓名:" + name + " 薪水:" + 12*sal ;
}
}
//Manager类
public class Manager extends Employee{
private double bonus;
public Manager(String name, double sal, double bonus) {
super(name, sal);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage(){
System.out.println("经理:" + getName() + "管理工作.");
}
@Override
public String getAnnual() {
return "经理" + super.getAnnual() + " 奖金" + bonus;
}
}
//OrdEmployee类
public class OrdEmployee extends Employee{
public OrdEmployee(String name, double sal) {
super(name, sal);
}
public void work(){
System.out.println("普通员工:" + getName() + "从事工作.");
}
@Override
public String getAnnual() {
return "普通员工" + super.getAnnual();
}
}
//主类
public class Test {
public static void main(String[] args) {
Test test = new Test();
Manager manager = new Manager("lili",10000,20000);
OrdEmployee ordEmployee = new OrdEmployee("xiaoli",7000);
Employee employee1 = new Manager("haha",15000,25000);
Employee employee2 = new OrdEmployee("xiaoha",8000);
System.out.println(test.showEmpAnnual(manager));
System.out.println(test.showEmpAnnual(ordEmployee));
System.out.println(test.showEmpAnnual(employee1));
System.out.println(test.showEmpAnnual(employee2));
test.testWork(manager);
test.testWork(ordEmployee);
test.testWork(employee1);
test.testWork(employee2);
}
public String showEmpAnnual(Employee e){
return e.getAnnual();
}
public void testWork(Employee e) {
if (e instanceof Manager) {
Manager manager = (Manager) e;
manager.manage();
} else if (e instanceof OrdEmployee) {
OrdEmployee ordEmployee = (OrdEmployee) e;
ordEmployee.work();
} else {
System.out.println("错误");
}
}
}
五 Super关键字
1 基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器。
2 基本语法
① 访问父类的属性,但不能访问父类的private属性;
例如:super.属性名;
② 访问父类的方法,不能访问父类的private方法;
例如:super.方法名(参数列表);
③ 访问父类的构造器;
例如:super(参数列表);只能放在构造器的第一句,只能出现一句。
3 super给编程带来的便利/细节
(1)调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化);
(2)当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果(从子类逐层向上【父类】查找)!
(3)super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A(子类) -> B(父类) -> C(爷爷类)(先找父类,再找爷爷类,若都没有再找子类);
4 super和this的比较
六 方法重写(overwrite)
1 基本介绍
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
2 注意事项和使用细节
(1)子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样;
(2)子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类;
比如:父类返回类型是Object,子类方法返回类型是String
(3)子类不能缩小父类方法的访问权限,但可以扩大。
3 方法重载和方法重写的区别和联系
七 Object类详解,垃圾回收机制
1 equals方法
(1)==和equals的对比
==是一个比较运算符:
① ==:既可以判断基本类型,又可以判断引用类型;
② ==:如果判断基本类型,判断的是值是否相等。例如:int i = 10; double d = 10.0;true
③ ==:如果判断引用类型,判断的是地址是否相等,及判断是不是同一个对象;
④ equals():是Object类中的方法,只能判断引用类型;
⑤ 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String。
//Object类中的equals方法
//判断对象/地址是否相同
public boolean equals(Object obj) {
return (this == obj);
}
//String类中的equals方法
//判断字符串的值是否一致
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
//Integer类的equals方法
//判断传入的值是否相等
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
(2)equals()方法练习题
Person p1 = new Person();
p1.name = "lyxlearn";
Person p2 = new Person();
p2.name = "lyxlearn";
system.out.println(p1 == p2); // ==在此判断的是对象(地址),不同,false
system.out.println(p1.name.equals(p2.name)); //true,因为p1.name是字符串,
//在Person类中重写了equals,判断的是字符串是否一样,同,true
system.out.println(p1.equals(p2)); //因为person没有重写equals方法,
//所以用的还是Object父类的该方法,所以判断的是地址,不同,false
String s1 = new String("asdf");
String s2 = new String("asdf");
system.out.println(s1.equals(s2)); //同上第二个
system.out.println(s1 == s2);//同上第一个
int it =65;
float fl = 65.0f;
System.out.println(it == fl); //true 值一样
char ch1 = 'A'; char ch2 = 12;
System.out.println(it == ch1); //字符的本质是数字,A字符对应的数字为65, true
System.out.println(12 == ch2); //同上
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); //判断地址,不同 f
System.out.println(str1.equals(str2)); //判断字符串是否一样,true
System.out.println("hello" == new java.sql.Date()); //类型不一致,直接false
2 hashCode方法
(1)语法
对象名.hashCode();
一般通过将调用对象的内部地址转换成一个整数来实现的,注意不能等价于地址。
(2)注意细节
① 提高具有哈希结构的容器的效率;
② 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的;
③ 两个引用,如果指向的是不同对象,则哈希值是不一样的;
④ 哈希值主要是根据地址来的,不能完全将哈希值等价于地址;
⑤ 在集合中,hashCode如果需要的话,也会重写。
3 toString方法
(1)基本介绍
默认返回:全类名 + @ +哈希值的十六进制
//Object类中的toString方法
//getClass().getName():全类名
//Integer.toHexString(hashCode()): 哈希值的十六进制
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
子类中往往重写toString方法(快捷键 alt + insert 选择toString后勾选即可),用于返回对象的属性信息,在重写了toString方法后,打印对象或者拼接对象时,都会自动调用该对象的toString形式。
当直接输出一个对象时,toString方法会被默认的调用。
public class ToString_ {
public static void main(String[] args) {
Monster monster = new Monster("小银角", 200, "巡山的");
System.out.println(monster.toString());
System.out.println(monster);
}
}
class Monster{
private String name;
private int age;
private String job;
public Monster(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
@Override
public String toString() {
return "Monster{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
4 finalize方法
(1)当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法(比如释放资源,数据库链接等,若不重写则直接调用父类Object的finalize方法),做一些释放资源的操作;
(2)什么时候被回收:当某一个对象没有任何引用时,则jvm就认定这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,就先调用finalize方法;
(3)垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动除法垃圾回收机制(不能保证一定,只是很大概率,因为垃圾回收机制可能在忙别的)。
八 断点调试(debug)
1 一个实际需求
(1)断点调试可以在一步一步的看源码执行的过程,从而发现错误所在;
(2)在断点调试过程中是运行状态,是以对象的运行类型来执行的。
2 介绍
(1)断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看到各个变量当前的值,如果出错的话,调试到出错的代码行即显示错误,停下,进行分析从而找到这个Bug;
(2)断点调试是程序员必须掌握的技能;
(3)断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的java水平。
3 快捷键
(1)F7(跳入:跳入方法内)
① 例子:Array.sort(arr);//其中arr是一个数组,对数组进行排序
就可以通过F7进入sort方法内,查看相关源码
② 若无法跳入有以下两种解决方法
a 使用alt + shift +F7进行强制跳入
b 通过设置(推荐)
(2)F8(跳过:逐行执行代码)
(3)shift + F8(跳出方法)
通常和F7跳入方法内连用,用于跳出方法。
(4)F9(resume:执行到下一个断点)
注意:断点支持在运行过程中动态地下断点。
F9尝使用于不在于两断点之间地内容,直接执行到下一个断点,不再逐行执行。
4 运行模块图
九 项目
1 零钱通(面向过程编程)
(1)完成显示菜单,并可以选择菜单,给出相应的提示;
(2)完成零钱通明细
三种方案①数组 ②对象 ③String拼接
(3)完成收益入账
(4)完成消费
(5)完成退出,改进代码
① 用户输入4退出时,给出提示"是否退出(y/n)",必须输入正确的y/n,否则循环输入指令,直到输入y或者n。
②在收益入账和消费时,判断金额是否合理,并给出相应的提示。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class SmallChangeSys {
//1.完成显示菜单,并可以选择菜单,给出相应的提示;
//2.完成零钱通明细
//三种方案(1):数组(2):对象(3)String拼接
//3.完成收益入账
//4.完成消费
//5.完成退出,改进代码
//(1)用户输入4退出时,给出提示"是否退出(y/n)",必须输入正确的y/n,
// 否则循环输入指令,直到输入y或者n。
//(2)在收益入账和消费时,判断金额是否合理,并给出相应的提示。
//(3)将面向过过程的代码改成面向对象的方法,编写SmallChangeSysOOP.java,
//完成改进后的1,2,3,4,5各个功能
//并使用SmallChangeSysApp.java,创建对象,调用方法,完成测试。
public static void main(String[] args) {
//定义相关变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String choseNum = "";
//2.完成零钱通明细
String details ="--------------零钱通明细--------------";
//3.完成收益入账
//定义新的变量(根据功能),完成功能驱动程序员增加新的变化和代码
double money = 0;//收入
double balance = 0;//余额
Date date = null;// date 是 Java.util.Date 的类,表示日期
//将date转换为中国常用的格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//4.完成消费
//定义新变量,保存消费的原因
String note ="";
//5.退出
//定义新变量,保存退出的标记
String choice = "";
do {
System.out.println("\n==============零钱通菜单==============");
System.out.println("\t\t\t" + "1 零钱通明细");
System.out.println("\t\t\t" + "2 收益入账");
System.out.println("\t\t\t" + "3 消费");
System.out.println("\t\t\t" + "4 退\t出");
System.out.print("请选择(1-4):");
choseNum = scanner.next();
//使用switch分支控制
switch (choseNum){
case "1":
System.out.println(details);
break;
case "2":
System.out.print("收益入账金额:");
money = scanner.nextDouble();
//money的范围应该校验,不可能为负数
if (money <= 0){
System.out.println("收益入账金额输入有误(小于等于0)");
break;
}
balance +=money;
date = new Date();//获取当前时间,需要SimpleDateFormat转换格式
//拼接收益入账信息到 details
details += "\n收益入账\t+" + money +" "+ sdf.format(date) +" 余额:" + balance;
break;
case "3":
if (balance == 0){
System.out.println("余额为0");
break;
}
System.out.print("消费金额:");
money = scanner.nextDouble();
//money的范围应该校验,不能超过余额balance
if (money <= 0 || money > balance){
System.out.println("消费金额有误(未在0-" + balance + "之间)");
break;
}
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
date = new Date();
//拼接消费信息到 details
details += "\n"+ note + "\t-" + money +" "+ sdf.format(date) +" 余额:" + balance;
break;
case "4":
while (true){
//y表示是,n表示否
System.out.print("是否退出(y/n):");
choice = scanner.next();
if ("y".equals(choice) ||"n".equals(choice)){
break;
} else {
System.out.println("输入有误,请重新输入(y/n)");
}
//第二个方案,将两个功能写在一段代码里,即判断①是否输入的为y/n;②输入的是y还是n
// if ("y".equals(choice)){
// loop = false;
// break;
// }else if ("n".equals(choice)){
// System.out.println("程序继续");
// break;
// }else {
// System.out.println("输入有误,请重新输入(y/n)");
// }
if ("y".equals(choice)){
loop = false;
}else {
System.out.println("程序继续");
}
break;
default:
System.out.println("输入号码有误,请重新输入(1-4).");
}
}while (loop);
System.out.println("使用愉快,欢迎下次登陆~");
}
}
2 零钱通(面向对象编程)
将面向过过程的代码改成面向对象的方法,编写SmallChangeSysOOP.java,完成改进后的1,2,3,4,5各个功能,并使用SmallChangeSysApp.java,创建对象,调用方法,完成测试。
//SmallChangeSysOOP类
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class SmallChangeSysOOP {
//定义相关变量
private boolean loop = true;
private Scanner scanner = new Scanner(System.in);
private String choseNum;
//2.完成零钱通明细
private String details ="--------------零钱通明细--------------";
//3.完成收益入账
//定义新的变量(根据功能),完成功能驱动程序员增加新的变化和代码
private double money;//收入
double balance;//余额
private Date date;// date 是 Java.util.Date 的类,表示日期
//将date转换为中国常用的格式
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//4.完成消费
//定义新变量,保存消费的原因
private String note;
//5.退出
//定义新变量,保存退出的标记
private String choice;
public boolean isLoop() {
return loop;
}
public void setLoop(boolean loop) {
this.loop = loop;
}
//1.完成显示菜单,并可以选择菜单,给出相应的提示;
public void ShowMenu(){
do {
System.out.println("\n==============零钱通菜单(OOP)==============");
System.out.println("\t\t\t" + "1 零钱通明细");
System.out.println("\t\t\t" + "2 收益入账");
System.out.println("\t\t\t" + "3 消费");
System.out.println("\t\t\t" + "4 退\t出");
System.out.print("请选择(1-4):");
choseNum = scanner.next();
switch (choseNum) {
case "1":
ShowDetails();
break;
case "2":
CompleteEntry();
break;
case "3":
CompleteConsume();
break;
case "4":
ExitSys();
break;
default:
System.out.println("输入号码有误,请重新输入(1-4).");
}
}while (loop);
}
//2.完成零钱通明细
public void ShowDetails(){
System.out.println(details);
}
//3.完成收益入账
public void CompleteEntry(){
System.out.print("收益入账金额:");
money = scanner.nextDouble();
//money的范围应该校验,不可能为负数
if (money <= 0){
System.out.println("收益入账金额输入有误(小于等于0)");
return;
}
balance +=money;
date = new Date();//获取当前时间,需要SimpleDateFormat转换格式
//拼接收益入账信息到 details
details += "\n收益入账\t+" + money +" "+ sdf.format(date) +" 余额:" + balance;
}
//4.完成消费
public void CompleteConsume(){
if (balance == 0){
System.out.println("余额为0");
return;
}
System.out.print("消费金额:");
money = scanner.nextDouble();
//money的范围应该校验,不能超过余额balance
if (money <= 0 || money > balance){
System.out.println("消费金额有误(未在0-" + balance + "之间)");
return;
}
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
date = new Date();
//拼接消费信息到 details
details += "\n"+ note + "\t-" + money +" "+ sdf.format(date) +" 余额:" + balance;
}
//5.完成退出
public void ExitSys(){
while (true){
//y表示是,n表示否
System.out.print("是否退出(y/n):");
choice = scanner.next();
if ("y".equals(choice) ||"n".equals(choice)){
break;
} else {
System.out.println("输入有误,请重新输入(y/n)");
}
//第二个方案,将两个功能写在一段代码里,即判断①是否输入的为y/n;②输入的是y还是n
// if ("y".equals(choice)){
// loop = false;
// break;
// }else if ("n".equals(choice)){
// System.out.println("程序继续");
// break;
// }else {
// System.out.println("输入有误,请重新输入(y/n)");
// }
}
if ("y".equals(choice)){
loop = false;
}else {
System.out.println("程序继续");
}
}
}
//SmallChangeSysApp测试类
public class SmallChangeSysApp {
public static void main(String[] args) {
SmallChangeSysOOP smallChangeSysOOP = new SmallChangeSysOOP();
smallChangeSysOOP.ShowMenu();
System.out.println("使用愉快,欢迎下次登陆~");
}
}
知识点
1 继承设计的基本思想
父类的构造器完成父类属性的初始化,子类的构造器完成子类属性的初始化。
2 查看JDK源码
放在想要查看的方法上面直接ctrl+B即可(这是我的快捷键)无快捷键可以右击,选择GO TO -> Declaration or Usages即可查看源码。
若无法查看,可能是因为jdk包没有导入,可以File -> Project Structure -> SDKs -> Sourcepath,点击右侧的加号,选择自己安装jdk的路径,导入src.zip和javafx-src.zip即可。
3 得到当前日期
先导入包:import java.util.Date
Date date = new Date; //此时得到的日期格式是外国格式
导入包:import java.text.SimpleDateFormat
然后创建对象、转换日期格式:
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//注意大小写
sdf.format(date);即可转换成功。