面向对象
toString重写
代码演示:
public class Test01
{
public static void main(String[] arg)
{
MyTime my = new MyTime(1000,2,3);
//未改写toString方法之前输出结果为:MyTime@54bedef2
//第一种:
//System.out.println(my.toString());
//第二种:默认会调用toString方法
System.out.println(my);
}
}
class MyTime
{
int year;
int month;
int day;
//无参构造
public MyTime(){
}
//有参构造
public MyTime(int year, int month, int day)
{
this.year = year;
this.month = month;
this.day = day;
}
//重写toString方法
public String toString()
{
return this.year + "年" +this.month + "月" + this.day + "日";
}
}
equals重写
代码演示:
public class Test05
{
public static void main(String[] arg)
{
Address addr1 =new Address("北京","朝阳区");
Address addr2 =new Address("北京","朝阳区");
Address addr3 =new Address("湖北","武汉市");
User use1 = new User("李某",addr1);
User use2 = new User("李某",addr2);
User use3 = new User("李某",addr3);
System.out.println(use1.equals(use2));
System.out.println(use1.equals(use3));
}
}
class User
{
String name;
Address add;
//无参构造
public User()
{
}
//有参构造
public User(String name, Address add)
{
this.name = name;
this.add = add;
}
//重写equals方法
public boolean equals(Object obj)
{
//如果obj为空直接返回false
//如果obj不是一个User,没必要比较了,直接返回false
if(obj == null || !(obj instanceof User))
{
return false;
}
//如果this 和obj保存的内存地址相同,没必要比较了,直接返回true。
//内存地址相同的时候指向的堆内存的对象肯定是同一个。
if(this == obj)
{
return true;
}
//将obj进行向下强转
User u = (User)obj;
//String引用类型比较,用equals方法
//相等返回true;不相等返回false
if (this.name.equals(u.name) && this.add.equals(u.add))
{
return true;
}
return false;
}
}
class Address
{
String city;
String street;
//无参构造
public Address()
{
}
//有参构造
public Address(String city, String street)
{
this.city = city;
this.street = street;
}
//重写equals方法
public boolean equals(Object obj)
{
//如果obj为空直接返回false
//如果obj不是一个Address,没必要比较了,直接返回false
if(obj == null || !(obj instanceof Address))
{
return false;
}
//如果this 和obj保存的内存地址相同,没必要比较了,直接返回true。
//内存地址相同的时候指向的堆内存的对象肯定是同一个。
if(this == obj)
{
return true;
}
//将obj向下强转。
Address add = (Address)obj;
//String引用类型比较,用equals方法
//相等返回true;不相等返回false
if(this.city.equals(add.city) && this.street.equals(add.street))
{
return true;
}
return false;
}
}
final(关键词)
final可以修饰变量以及方法,还有类等。
- final修饰的局部变量只能赋值一次。
- final修饰的实例变量必须手动赋值初始化。(该实例变量值不会随着对象的变化而变化。所以建议添加static修饰,节约内存空间)。
- static final联合修饰的变量称为"常量"。常量名建议全部大写,每个单词之间采用下划线衔接。
- final修饰的方法无法被覆盖,重写。
- final修饰的类无法继承。
- final修饰的引用一旦指向某个对象,则不能再重新指向其它对象,但该引用指向的对象内部的数据是可以修改的。(在该方法执行过程中,该引用指向对象之后,该对象不会被垃圾回收器回收。直到当前方法结束,才会释放空间)。
注意:常量和静态变量都是存储在方法区,并且都是在类加载时初始化。
常量一般都是公开的(public)。
代码演示:
public class FinalTest01
{
public static void main(String[] arg)
{
//final修饰变量,只能赋值一次:
final int i;
//final修饰引用:
final A aa = new A();
//final修饰引用指向的对象内部数据可以被赋值修改的。
aa = 10;
//final修饰的引用无法重新指向其他对象。
aa = new B();//错误!
}
}
//final修饰类名,无法继承:
final class A
{
//final修饰方法名无法被覆盖和重写。
public final void a()
{
}
}
final class B
{
//以后代码不会这么编写20~36行。
//final修饰实例变量,必须手动赋值。
//final int bb;//错误,编译器报错
final int bb = 10;//正确。
//实例变量在构造方法执行时赋值。
final int bbb;
public B()
{
this.bbb = 20;
}
//实例变量既然使用了final修饰,说明该实例变量值不会随着对象的变化而变化
//该实例变量前面应该添加:static关键字,变为静态,存储在方法区。
//static final联合修饰的变量称为"常量"。常量名建议全部大写,每个单词之间采用下划线衔接。
//常量一般是公开的(public)
public static final bb_2 = 10;
}
抽象类和接口的区别
抽象类(abstract)
- 抽象类简介:
类和类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。 - 抽象类属于引用数据类型。
- 抽象类定义:
语法:
[修饰符列表] abstract class 类名
{
类体
} - 抽象类无法实例化,无法创建对象。所以抽象类是用来被子类继承的。
抽象类是类与类之间有共同特征,将这些具有共同特征的类再进一步抽象形成了抽象类。由于类本身不存在的,所以抽象类无法创建对象。 - final 和 abstract不能同时出现!!
- 抽象类的子类可以抽象化
- 抽象类虽然无法实例化,但抽象类有构造方法,它是供子类使用的。
抽象方法
抽象方法表示没有实现的方法,没有方法体的方法。
抽象方法的特点是:
- 没有方法体,以分号结尾。
- 前面修饰符列表中有abstract关键字。
- 抽象类中不一定需要抽象方法,但抽象方法必须出现在抽象类中。
注意:一个非抽象类继承抽象类,必须将抽象类中的抽象方法重写/覆盖。
代码演示:
public class AbstractTest02
{
public static void main(String[] arg)
{
//多态:父类型引用指向子类型对象
Animal a = new Bird();//向上转型(自动类型转换)
//这是面向抽象编程。
//调用a.xxx;a的类型是Animal,Animal是抽象的。
//面向抽象编程,不要面向具体编程,降低程序耦合度,提高程序的扩展力。
//这种编译思想符合OCP原则。
//编译父类,运行子类!!!!!
a.move();//这里运行的是Bird子类中的move方法
//多态:父类型指向子类型对象
Animal c = new Cat();//向上转型(自动类型转换)
//编译父类,运行子类
c.move();//这里运行的是Cat子类中的move方法
}
}
//动物类(抽象类)
abstract class Animal
{
//抽象方法
public abstract void move();
}
class Bird extends Animal
{
//抽象方法必须出现在抽象类中
//报错:AbstractTest02.java:15: 错误: Bird不是抽象的, 并且未覆盖Animal中的抽象方法main()
//由于继承了抽象类Animal中的抽象方法,需要进行重写,覆盖,否则编译器报错.
public void move(){//更正后的正确代码
System.out.println("鸟儿会飞翔");
}
}
//抽象方法必须出现在抽象类中
//如果抽象类继承抽象类,则不需要重写抽象方法
//但是抽象类中无法实例化对象!!!
/*
abstract class Bird extends Animal
{
}
*/
class Cat extends Animal
{
public void move()
{
System.out.println("猫儿在走猫步");
}
}
面试题:java语言中凡是没有方法体的方法都是抽象方法。
错误。
Object类中就有很多方法都没有方法体,都是以“;”结尾,但他们都不是抽象方法。
因为底层调用了c++写的动态链接库程序。
修饰符列表中有一个native。表示调用JVM本地程序。
接口(interface):
基础语法
- 接口也是一种“引用数据类型”。编译之后也是一个class字节码文件。
- 接口是特殊的抽象类(完全抽象),接口通常提取的是行为动作。-----抽象类是半抽象。
- 接口定义,语法是:
[修饰符列表] interface 接口名() - 接口支持多继承,一个接口可以继承多个接口。
- 接口和接口之间在进行强制转换时,没有继承关系也可以强转
- 接口中只有常量和抽象方法。
- 接口中所有元素都是public修饰的。(都是公开的)
- 接口中的抽象方法定义时: public abstract修饰符可以省略。
- 接口中的方法不能有方法体。
- 接口中的常量public static final 可以省略。
- 当继承extends与实现implements同时存在时:extends 在前,implements 在后。
- 当一个非抽象类继承抽象类必须将抽象类中的所有方法全部实现(覆盖,重写)。用implements实现,不是extends。
- 一个类可以实现多个接口
- 使用接口编写代码时可以使用多态(父类型引用指向子类型对象)。
- 接口和接口之间在进行强制转换时,无论是向上还是向下转型,没有继承关系也可以强转。(见代码-----第二段)
代码演示:
第一段
public class Test02
{
public static void main(String[] arg)
{
//错误!!! MyMath是抽象的,无法实例化。
//new MyMath();
//多态:父类型的引用指向子类型的对象
MyMath mm = new MyMathImple();
int result1 = mm.sum01(10, 20);
int result2 = mm.sum02(10, 20);
System.out.println(result1);
System.out.println(result2);
}
}
//特殊的抽象类,完全抽象,叫做接口
interface MyMath
{
//常量
//public static final double PI = 3.14159265;
//public static final 可以省略
//所以接口中随便写一个变量都是常量
//因为在interface中默认添加public static final。
double PI = 3.14;
//抽象方法
//public abstract int sum(int a, int b);
//接口中既然都是抽象方法,那么public abstract可以省略。
//接口抽象方法不能带有主体。
int sum01(int a, int b);
int sum02(int a, int b);
}
//编写一个类(这个类是一个“非抽象”的类)
/*
//错误
class MyMathImpl extends MyMath
{
}
*/
//修改方案一:
/* 抽象类继承接口类。
abstract class MyMathImple implements MyMath
{
}
*/
//修改方案二:
//重写接口(完全抽象)内的方法。
class MyMathImple implements MyMath
{
//这里需要(实现)重写/覆盖。
//去掉public会报错,因为访问权限只能更高,不能更低。
public int sum01(int a, int b)
{
return a + b;
}
public int sum02(int a, int b)
{
return a - b;
}
}
第二段
public class Test03
{
public static void main(String[] arg)
{
//多态:父类型A引用指向子类型D的对象
A a = new D();
B b = new D();
//调用其它接口中的方法,需要转型(接口转型)
//方法一:
B b2 = (B)a;
b2.m2();
//方法二:(直接向下转型,)
D d = (D)a;
d.m2();
d.m1();
//
A aa = new E();
aa.m1();
}
}
interface A
{
void m1();
}
interface B
{
void m2();
}
//实现多个接口,其实就类似于多继承,D继承A,B,C
class D implements A, B
{
//实现(重写/覆盖)A接口中的m1方法。
public void m1(){
System.out.println("m1..");
}
//实现(重写/覆盖)A接口中的m2方法。
public void m2(){
System.out.println("m2..");
}
}
class C
{
}
//当继承extends与实现implements同时存在时:extends 在前,implements 在后。
class E extends C implements A
{
public void m1()
{
System.out.println("m1..");
}
}
ClassCastException异常见链接:ClassCastException异常
接口在开发中的作用:
- 接口的作用类似于多态在开发中的作用。
多态:面向抽象编程,不要面向具体变成,降低程序的耦合度,提高程序的扩展力。 - 接口:面向抽象(接口)编程,降低程序耦合度。接口使用离不开多态机制(接口+多态)
- 接口是将调用者和实现者解耦合。
调用者面向接口调用
实现者面向接口编写实现。
类型与类型之间的关系: is a, has a, like a
is a(继承):
Cat is a Animal(猫是一个动物)
凡是能满足is a的表示“继承关系”
A extends B
has a(关联):
I has a Pen(我有一只笔)
凡是能够满足 has a关系的表示“关联关系”
关联关系通常以“属性”的形式存在。
A{
B b;
}
like a(实现):
Cooker like a FoodMenu(厨师像一个菜单一样 ---- 菜单有的,厨师都能做)
司机像导航
凡是能够满足like a关系的表示“实现关系”
实现关系通常是:类实现接口。
A implements B
抽象类与接口的语法区别:
- 抽象类是半抽象的,接口是完全抽象的。
- 抽象类有构造方法,接口没有构造方法。
- 类和类之间只能单继承,接口和接口之间支持多继承
- 一个抽象类只能继承一个类(单继承),一个类可以同时实现多个接口。
- 接口中只允许出现常量和抽象方法-----接口一般都是对“行为”的抽象。
package 和 import
package :(Java中的包机制)
作用:为了方便程序的管理,不同功能的类分别存放在不同的包下。
使用:
package是一个关键字,后面+包名
代码:package com.bjpowernode.javase.chapter17;
包名:
一般采用公司域名倒序的方式(公司域名具有全球唯一性)。
命名规范:
公司域名倒序 + 项目名 + 模块名 + 功能名
注意: package语句只允许出现在Java源代码中的第一行。
编译: javac -d . HalloWorld.java
javac 负责编译的命令。
-d 带包编译
. 代表编译之后生成的东西放到当前目录下(点代表当前目录)
HalloWorld.java 被编译的Java文件名。
运行:
Java 完整类名
java com.bjpowernode.javase.chapter17.HalloWorld
com.bjpowernode.javase.chapter17.HalloWorld 是完整类名
HalloWorld 是简类名
import 将需要的类导入。
使用:
Java.lang不需要,同包下不需要。其它都需要。
A类中使用B类。
A类和B类不再同一个包下,需要使用import。
如果A类和B类都在同一个包下,不需要import。
imprt语句只能出现在class上面,backage下面。
注意:java.lang.* ,这个包下的不需要导入。
访问控制权限
访问控制权限的范围:
修饰:
属性(4个都能用)
方法(4个都能用)
类(public 和默认都能用,其它不行)
接口(public 和默认能用,其它不行)
匿名内部类:
内部类:在类的内部又定义了一个新的类,被称为内部类。
内部类分类:
静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量
- 尽量不要使用内部类,可读性差,无法二次复用。
- 匿名内部类属于局部内部类的一种,因为这种类没有名字而得名:匿名内部类。
代码演示:
class Test01{
// 静态变量
static String country;
// 该类在类的内部,所以称为内部类
// 由于前面有static,所以称为“静态内部类”
static class Inner1{
}
// 实例变量
int age;
// 该类在类的内部,所以称为内部类
// 没有static叫做实例内部类。
class Inner2{
}
// 方法
public void doSome(){
// 局部变量
int i = 100;
// 该类在类的内部,所以称为内部类
// 局部内部类。
class Inner3{
}
}
public void doOther(){
// doSome()方法中的局部内部类Inner3,在doOther()中不能用。
}
// main方法,入口
public static void main(String[] args){
// 调用MyMath中的mySum方法。
MyMath mm = new MyMath();
/*
Compute c = new ComputeImpl();
mm.mySum(c, 100, 200);
*/
//合并(这样写代码,表示这个类名是有的。类名是:ComputeImpl)
//mm.mySum(new ComputeImpl(), 100, 200);
// 使用匿名内部类,表示这个ComputeImpl这个类没名字了。
// 这里表面看上去好像是接口可以直接new了,实际上并不是接口可以new了。
// 后面的{} 代表了对接口的实现。
// 不建议使用匿名内部类,为什么?
// 因为一个类没有名字,没有办法重复使用。另外代码太乱,可读性太差。
mm.mySum(new Compute(){
public int sum(int a, int b){
return a + b;
}
}, 200, 300);
}
}
// 负责计算的接口
interface Compute{
// 抽象方法
int sum(int a, int b);
}
// 你自动会在这里编写一个Compute接口的实现类
/*
class ComputeImpl implements Compute{
// 对方法的实现
public int sum(int a, int b){
return a + b;
}
}
*/
// 数学类
class MyMath{
// 数学求和方法
public void mySum(Compute c, int x, int y){
int retValue = c.sum(x, y);
System.out.println(x + "+" + y + "=" + retValue);
}
}
数组
- Java语言中的数组是一种引用数据类型。不属于基本数据类型,数组的父类是Object.。
- 数组是一个容器,可以同时容纳多个元素(数组是一个数据的集合。)字面意思:“一组数据”。
- 数组当中可以存储基本数据类型的数据,也可以储存引用数据类型的数据。
- 数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)
- 数组当中如果存储的是“Java对象”的话,实际上存储的是对象的“引用(内存地址)”,数组中不能直接存储Java对象。
- 数组一旦创建,在Java中规定,长度不可变。
- 数组分类:一维数组,二位数组,三维数组,多维数组…(一维数组较多,二维数组偶尔使用)
- 所有的数组对象都有lenth属性(Java自带),用来获取数组中元素的个数。
- Java中的数组要求数组中元素的类型统一。比如int类型数组只能存储int类型,person类型数组只能存储person类型。
- 数组在内存存储时,数组中的元素内存地址时连续的(存储的每一个元素都是有规则的),数组实际上是一种数据结构。
- 数组中每个元素都是有下标的,下标从0开始,以1递增,最后一个元素的下标是(length - 1 ), 数组中的首元素内存地址作为数组的内存地址。
- 数组的优点:查询/查找某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
- 数组缺点:
1. 在数组上随机删除或者增加元素时,效率较低,因为随机增删元素会涉及到后面元素统一向前或向后位移的操作。
2. 数组不能存储大数据量:因为很难在内存空间上找到一块特别大的连续的内存空间。
面试题:为什么检索效率高?
1. 每个元素的内存地址在空间存储上是连续的。
2. 每个元素的类型相同,即所占空间大小相同。
3. 知道第一个元素的内存地址,元素所占空间的大小,及其下标,可以通过一个数学表达式就可以计算出某个下标上元 素的内存地址,直接通过内存地址定位元素,所以检索效率最高。
一维数组:
语法格式:
int [] array1;
Object[] array2;
初始化一维数组:包括静态初始化和动态初始化。
静态初始化:
int[] array ={100,200,500};
动态初始化:
//这里的5表示数组的个数
//初始化一个5个长度的int类型数组,每个元素默认值为0
int[] array = new int[5];
//初始化6个长度的String类型数组,每个元素默认值为null
String[] array = new String[6];
当创建数组时,确定数组中存储哪些具体的元素时,采用静态初始化方式。
当创建数组时,不确定将来数组中存储哪些数据,你可以采用动态初始化方式,预先分配内存空间。
数组扩容:
在java开发中,数组长度一旦确定则不可变,数组满了需要扩容。
扩容:(数组扩容效率低。因为涉及到拷贝的问题。)
先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组中。
建议:
在创建数组对象的时候预估多长合适,最好预估准确,这样可以减少数组的扩容次数,提高效率。
代码演示:
一维数组:(用一维数组模拟入栈,出栈)
package com.bjpowernode.javase.array;
public class HomeWork01_2 {
public static void main(String[] args) {
//MyStack stack = new MyStack(10,-1);//这里是给有参构造传递参数
MyStack stack = new MyStack();
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
}
}
class MyStack{
//也可以在后面无参构造中默认初始化容量:this.elements = new Object[10];
//private Object[] elements = new Object[10];
private Object[] elements;
private int index;
//无参构造
public MyStack()
{
//建议直接在无参构造中给一维数组长度,数据类型等进行初始化,
//一维数组初始化
//默认初始化容量为4
this.elements = new Object[4];
//默认初始化index为-1,-1表示指向栈顶元素。
this.index = -1;
}
//有参构造
/*
public MyStack(Object length, int index) {
//这里是将main方法中的参数传进来,给一维数组长度进行赋值,有些许多此一举。
this.elements = new Object[Integer.parseInt(String.valueOf(length))];
this.index = index;
}*/
//elements 的Setter and Getter方法
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
//index的setter and getter
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public void push(Object obj){
if (index >= elements.length -1)
{
System.out.println("压栈失败,栈已满");
return;
}
index++;
elements[index] = obj;
//所有System.out.println()方法执行时,如果输出引用,自动调用toString()方法。
System.out.println("压栈"+obj+"元素成功,栈帧指向"+index);
}
public void pop()
{
if (index < 0)
{
System.out.println("弹栈失败");
return;
}
System.out.println("弹栈"+elements[index]+"元素成功,栈帧指向"+(index--));
}
}
自创版:(用一维数组模拟栈,出栈,入栈)
package com.bjpowernode.javase.array;
import java.util.Scanner;
public class HomeWork01 {
public static void main(String[] args) {
Mystack m = new Mystack(4);
//Object[] obj= new Object[4];
m.push();
System.out.println("=======");
m.pop();
}
}
class Mystack{
Object[] elements;
//无参构造
public Mystack(){
}
//有参构造
public Mystack(Object length)
{
this.elements = new Object[Integer.parseInt(String.valueOf(length))];
}
//压栈
public void push()
{
for (int i = 1; i > 0 ; i++) {
System.out.print("请输入第"+ i +"个元素数据:");
Scanner s = new Scanner(System.in);
elements[i-1] = s.next();
// System.out.println("栈帧为:"+elements[i - 1]);
if (i < elements.length) {
System.out.println("栈内存还剩" + (elements.length - i));
}else {
System.out.println("栈内存以用完,无法压栈");
break;
}
}
System.out.print("输入所有数据为:");
for (int i = 0; i < elements.length ; i++) {
System.out.print(elements[i] + "\t");
}
System.out.println("");
}
//出栈
public void pop()
{
for (int i = elements.length - 1; i < elements.length ; i--) {
elements[i] = null;
if(i > 0) {
System.out.println("出栈数据为:");
for (int j = 0; j < elements.length; j++) {
System.out.print(elements[j] + "\t");
}
System.out.println("");
//System.out.println("出栈所有数据为:"+elements[i]);
}else {
for (int j = 0; j < elements.length; j++) {
System.out.print(elements[j] + "\t");
}
System.out.println("栈已空,弹栈失败");
break;
}
}
}
}
数组扩容:
package com.bjpowernode.javase.array;
public class ArrayTest05 {
public static void main(String[] args) {
//拷贝:System.arraycopy(5个参数)
//拷贝源:(从这个数组内拷贝)
int[] src = {1,2,312,435,546};//静态数组
//拷贝目标(拷贝到这个数组上)
int[] dest = new int[10];//动态初始化一个长度为10的数组,默认值为0
//调用JDK System类中的arraycopy方法完成拷贝.
/*拷贝数组中的部分数据
src 表示被拷贝的数组源,1 表示从被拷贝的数组源源的第1个起始位置开始
dest 表示目标源(接收原数组数据的数组), 2 表示从第2个位置开始进行粘贴
3 表示拷贝粘贴数组的长度。
System.arraycopy(src, 1, dest, 2, 3);
//遍历目标数组
for (int i = 0; i <dest.length ; i++) {
System.out.println(dest[i]);
}
*/
//拷贝数组中的所有元素
System.arraycopy(src, 0, dest, 0, src.length);
for (int i = 0; i <dest.length ; i++) {
System.out.println(dest[i]);
}
System.out.println("==================");
//引用数据类型
// 拷贝源:
String[] str1 = {"aj","asd","da"};
//粘贴到:
String[] str2 = new String[5];
//拷贝数组中的所有元素。
System.arraycopy(str1,0,str2, 0,str1.length);
for (int i = 0; i <str2.length ; i++) {
System.out.println(str2[i]);
}
System.out.println("======================");
//最终输出结果为:地址!!!
//拷贝源:
Object[] obj1 = {new Object(),new Object(),new Object()};
//粘贴:
Object[] obj2 = new Object[5];
//拷贝数组中所有元素
System.arraycopy(obj1, 0, obj2, 0,obj1.length);
//遍历
for (int i = 0; i <obj2.length ; i++) {
System.out.println(obj2[i]);
}
}
}
二维数组及多维数组
二维数组是一个特殊的一维数组,特殊在这个二维数组当中的每个元素都是一个一维数组。
N维数组是一个特殊的(N-1)数组,特殊在这个(N-1)维数组中的每一个元素都是一个(N-2)维数组。
语法格式:
//二维数组:
int[][] a = {{100,23,452},{213,36,123},{87,324,45}};
代码演示:(运用到二维数组的酒店管理系统)
自己琢磨,无说明。
package com.bjpowernode.javase.array;
import java.util.Scanner;
import javax.swing.*;
public class HotelMgt {
public static void main(String[] args) {
Hotel hotel = new Hotel();
hotel.print();
System.out.println("请输入功能编号:【1】查看\t【2】订房\t【3】退房\t【4】退出系统");
while (true) {
Scanner s = new Scanner(System.in);
System.out.print("请输入功能编号:");
int i = s.nextInt();
switch (i) {
case 1:
hotel.print();
break;
case 2:
System.out.print("请输入订房房间号:");
Scanner roomNo = new Scanner(System.in);
hotel.order(roomNo.nextInt());
break;
case 3:
System.out.println("请输入退房房间号:");
Scanner roomNo2 = new Scanner(System.in);
hotel.exit(roomNo2.nextInt());
break;
case 4:
System.out.println("欢迎下次使用,再见!");
return;
default:
System.out.println("输入功能编号有误,请重新输入:");
}
}
}
}
class Room{
private int no;
private String type;
private boolean status;
public Room(){
}
public Room(int no, String type, boolean status) {
this.no = no;
this.type = type;
this.status = status;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String toString() {
return no +type+(status ? "空闲" : "占用");
}
public boolean equals(Object obj){
if (obj == null || !(obj instanceof Room)){
return false;
}
if (this == obj){
return true;
}
Room room = (Room)obj;
return (this.getNo() == room.getNo());
}
}
class Hotel{
private Room[][] rooms;
public Hotel(){
rooms = new Room[3][10];
for (int i = 0; i <rooms.length ; i++) {
for (int j = 0; j <rooms[i].length ; j++) {
if (i == 0){
rooms[i][j] = new Room((i+1)*1000+j+1,"单人间",true);
}else if (i == 1){
rooms[i][j] = new Room((i+1)*1000+j+1,"标准间",true);
}else if(i == 2){
rooms[i][j] = new Room((i+1)*1000+j+1,"豪华间",true);
}
}
}
}
public void print(){
for (int i = 0; i <rooms.length ; i++) {
for (int j = 0; j <rooms[i].length ; j++) {
System.out.print(rooms[i][j]+"\t");
}
System.out.println("");
}
}
public void exit(int roomNo){
Room room = rooms[roomNo/1000-1][roomNo%1000-1];
room.setStatus(true);
System.out.println(roomNo+"已退房");
}
public void order(int roomNo){
Room room = rooms[roomNo/1000-1][roomNo%1000-1];
room.setStatus(false);
System.out.println(roomNo+"已入住");
}
}
数组元素查找:
二分法查找:(先排序)
- 二分法查找建立在排序的基础上。
- 二分法效率高于“傻瓜式查找:一个一个找”。
代码演示:
import java.util.Scanner;
public class ArraySearch {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.print("请输入你要找的元素:");
int ss = s.nextInt();
int[] arr = {2,3,1,8,4,6,5,1};
//方法一:(傻瓜式)线形查找
Math m = new Math();
int i = m.math(ss,arr);
System.out.println((i == -1) ? "未找到该数据,请重新输入!" : "下标为:"+i);
//方法二:二分法查找:先排序后查找
Math02 m2 = new Math02();
int i2 = m2.math02(ss,arr);
System.out.println((i2 == -1) ? "未找到该数据,请重新输入!" : "下标为:"+i2);
}
}
//线形查找
class Math{
public int math(int ss,int[] arr){
for (int i = 0; i <arr.length ; i++) {
if (ss == arr[i]){
return i;
}
}
return -1;
}
}
//二分法查找
class Math02{
public int math02(int ss,int[] arr){
for (int i = 0; i <arr.length - 1 ; i++) {
int min = i;
for (int j = i+1; j <arr.length ; j++) {
// count2++;
if (arr[j] < arr[min]){
min = j;//最小值元素的下标是j。
}
}
//当i和min相等时,表示最初猜想是对的。
//当i和min不相等时,表示最初猜测时错的,有比这个元素更小的元素。
//需要拿这个更小的元素和最左边的元素交换位置。
if(min != i)
{
int temp;
temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
//数组遍历
/*
for (int i = 0; i <arr.length ; i++) {
System.out.println(arr[i]);
}
*/
//二分法查找
//表示开始的下标
int begin = 0;
//表示结束的下标
int end = arr.length-1;
//开始元素的下标只要在结束元素下标的左边,就有机会继续循环。
while (begin <= end) {
//中间元素的下标
int mid = (begin + end) / 2;
if (arr[mid] == ss) {
return mid;
} else if (arr[mid] < ss) {
//目标在“中间”的右边
//开始元素下标需要发生变化(开始元素的下标需要重新赋值)
begin = mid + 1;//一直增加
} else {
//目标在“中间”的左边
//修改结束元素的“下标”
end = mid - 1;//一直减
}
}
return -1;
}
}
常用算法:
冒泡排序:(BubbleSort)
首先从数组最左边数据开始,取第0号位置(左边)的数据和第1号位置(右边)的数据,如果左边的数据大于右边的数据,则进行交换,反之则不交换。
然后取第一个位置的数据和第二个位置的数据,进行比较,如果左边大于右边,则进行交换,反之则不交换。
代码演示:
public class BubbleSort {
public static void main(String[] args) {
//这是int类型的数组对象
int arr[] = {2123,323,2344,535,61,37};
int count = 0;
int counts = 0;
//6条数据,循环5次,每次循环将第一个数与后面的数依次比较,并取出最大值放在最右边,
// 下次循环排除最大值。将剩下的数据按照第一次循环一样依次比较。
for (int i = arr.length - 1; i >0 ; i--) {
//内部循环就是将数字进行比较,并交换位置,将最大值放在右边
counts ++;
for (int j = 0; j <i ; j++) {
count++;
if (arr[j] > arr[i])
{
int temp;
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
System.out.println("比较次数:" + count + "外部循环次数:"+ counts);
for (int i = arr.length - 1; i >0 ; i--) {
System.out.print(arr[i] + "\t>\t");
}
}
}
选择排序:(SelectSort)
选择排序比冒泡排序的效率高。高在交换位置的次数上。选择排序交换位置是有意义的。
循环一次,然后找出参加比较的这堆数据中最小的,拿着这个最小的值和最前面的数据交换位置。
代码演示:
public class SelectSort {
public static void main(String[] args) {
int[] arr = {251,23,435,565,123};
// int count2 = 0;
// int count = 0;
//5个数据只需要循环四次,所以是arr.length-1
//i是一个参与比较的这堆数据中的起点下标。
for (int i = 0; i <arr.length - 1 ; i++) {
int min = i;
for (int j = i+1; j <arr.length ; j++) {
// count2++;
if (arr[j] < arr[min]){
min = j;//最小值元素的下标是j。
}
}
//当i和min相等时,表示最初猜想是对的。
//当i和min不相等时,表示最初猜测时错的,有比这个元素更小的元素。
//需要拿这个更小的元素和最左边的元素交换位置。
if(min != i)
{
int temp;
temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
// count++;
}
}
// System.out.println("交换位置次数"+count);
// System.out.println("比较次数" + count2);
//数组遍历
for (int i = 0; i <arr.length ; i++) {
System.out.println(arr[i]);
}
}
}
数组工具类Arrays
所有方法都是静态的,直接用类名调用
主要使用的是两个方法:
二分法查找,排序
以后要看文档,不要死记硬背。
代码演示:
import java.util.Arrays;
public class ArraysTest01 {
public static void main(String[] args) {
int[] arr = {3,5,2,1,5,7,9,6,0};
//调用Object中的Arrays.sort方法排序:
Arrays.sort(arr);
//二分法查找:
int index = Arrays.binarySearch(arr,5);
System.out.println(index == -1 ? "不存在" : "该元素下标" + index);
}
}
String
- String表示字符串类型,属于引用数据类型,不属于基本数据类型。
- 在Java中使用双引号括起来的都是String类型。例如:“abc”。
- Java中规定,双引号括起来的都是不可变的。
- 在JDK当中双引号括起来的字符串都是存储在方法区内
面试题:
public class StringTest02 {
public static void main(String[] args) {
//这一共new了几个对象?
//5个:3个堆内存中的String对象,还有2个方法区常量池中的"hello"和"hahaha".
String a = new String("hello");
String b = new String("hello");
String c = new String("hahaha");
}
}
关于String类中的常用方法
代码演示:
import java.util.Arrays;
public class StringTest03 {
public static void main(String[] args) {
//charAt表示返回指定索引处的char值,括号内是索引(下标).
//"中国人"是一个字符串String对象,所以可以"对象.引用"
char c = "中国人".charAt(1);
System.out.println(c);
//compareTo表示按字典顺序比较两个字符串,返回值类型为int,并可以看出谁大谁小
System.out.println("abc".compareTo("abc"));//0
int result = "cdf".compareTo("atv");//2
System.out.println(result);
//contains: 当且仅当此字符串包含指定的 char 值序列时,返回 true.返回值类型为boolean
System.out.println("halloWorld".contains("a"));//true
//endsWith: 判断此字符串是否以指定的后缀结束.返回值类型为:boolean.
System.out.println("halloworld".endsWith("world"));//true
//startsWith:判断此字符串是否以某个字符串开始.返回值类型为:boolean.
System.out.println("halloworld".endsWith("world"));//false
//equalsIgnoreCase:将此 String 与另一个 String 比较,不考虑大小写.
System.out.println("HalloWorld".equalsIgnoreCase("halloworld"));//true
//getBytes() : 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
byte[] b = "dskfa".getBytes();
//遍历数组:
//方法一:foreach:
for(int i : b){
System.out.print(i + "\t");//100 115 107 102 97
}
//方法二:利用jdk自带方法:java.util.Arrays.toString
System.out.println(Arrays.toString(b));//[100, 115, 107, 102, 97]
//int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引.
System.out.println("dahagdsasdhgasdgahsdgyad".indexOf("a"));//1
//int lastIndexOf(String str):返回某个字符串在当前字符串中最后一次出现的索引(下标)
System.out.println("asdasdasdassdfsdczxcasdadQFRQ".lastIndexOf("a"));//23
//isEmpty():判断字符串是否为空字符串,如果为空则返回true.
String s = "";
System.out.println(s.isEmpty());//true
//int length()
//面试题:判断数组长度和判断字符串长度不一样。
//判断数组长度是length属性,判断字符串长度是length()方法
System.out.println("sdad".length());//4
//replace(CharSequence target, CharSequence replacement)
//将字符串中与target内容相同的所有字符全部替换成replacement的字符串内容
//String的父接口是:CharSequence
String newString = "李李,你好,你好".replace("你好", "Hallo");
System.out.println(newString);//"李李,hallo,hallo"
//split(String regex) :将字符串进行以regex内容进行拆分并以数组形式存储
String[] newString02 = "1,2,3,456,123,586".split(",");
for (int i = 0; i <newString02.length ; i++) {
System.out.print(newString02[i] + "\t");//1 2 3 456 123 586
}
//substring(int beginIndex) :截取字符串,参数是起始下标.
System.out.println("www.baidu.com".substring(5));//aidu.com
// String substring(int beginIndex, int endIndex):
//beginIndex:包括该位置
//endIndex :不包括该位置
System.out.println("www.baidu.com".substring(3,7));//.bai
//char[] toCharArray() :将此字符串转换为一个新的cahr数组
char[] chars = "我是中国人".toCharArray();
for (int i = 0; i <chars.length ; i++) {
System.out.print(chars[i] + "\t");//我 是 中 国 人
}
//toLowerCase() :使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
System.out.println("ABCDEFG".toLowerCase());//abcdefg
//toUpperCase():使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
System.out.println("asdasd".toUpperCase());//ASDASD
//trim() :去除字符串前后空白
System.out.println(" Hello World ".trim());//Hello World
//valueOf:将非字符串转换成"字符串"
//该方法时String类型中唯一一个静态方法,不需要new对象
String s1 = String.valueOf(100);//
String s2 = String.valueOf(true);
System.out.println(s1);//100
System.out.println(s2);//true
//当这个静态valueOf()方法,参数是一个对象时,会自动调用该对象的toString方法。
//没有重写Customer类中的toString时,输出结果时对象内存地址。
String s3 = String.valueOf(new Customer());
//本质上:System.out.println()这个方法在输出任何数据的时候都会先转换成字符串,再输出
System.out.println(s3);//com.lt.javase.string.Customer@27bc2616
}
}
class Customer{
public String toString() {
return "我是VIP";
}
}
StringBuffer
语法:
如果以后需要进行大量字符串的拼接操作,建议使用JDK中自带的java.lang.StringBuffer java.lang.StringBuilder
StringBuffer底层实际上是一个byte[]数组。
往StringBuffer中房字符串,实际上时放到byte[]数组中。
StringBuffer的初始化容量是16.
优化StringBuffer性能(自动扩容效率太低,扩容越少,效率越高):
* 在创建StringBuffer时尽可能顶一个初始化容量
* 最好减少底层数组的扩容次数。预估一下给一个大一些的初始化容量。
代码演示:
/*
* 如果以后需要进行大量字符串的拼接操作,建议使用JDK中自带的
* java.lang.StringBuffer java.lang.StringBuilder
* 优化String性能:
* 在创建StringBuffer时尽可能顶一个初始化容量
* 最好减少底层数组的扩容次数。预估以下给一个大一些的初始化容量。
* */
public class StringBufferTest01 {
public static void main(String[] args) {
//StringBuffer底层实际上是一个byte[]数组。
//往StringBuffer中房字符串,实际上时放到byte[]数组中。
//StringBuffer的初始化容量是16.
StringBuffer stringBuffer = new StringBuffer("abc");
//拼接字符串,以后拼接字符串统一调用append()方法
//append是追加的意思。
stringBuffer.append("a");
//append方法底层在进行追加的时候,如果byte数组满了,会自动扩容。
System.out.println(stringBuffer);
//指定初始化(capacity)容量的StringBuffer对象(字符串缓冲区对象)
StringBuffer sb = new StringBuffer(100);
sb.append("hello");
sb.append(" world");
System.out.println(sb);
}
}
StringBuilder与StringBuffer的区别:
- java.lang.StringBuild
- StringBuffer和StringBuilder区别:
- StringBuffer方法中都有synchronized关键字修饰,表示StringBuffer在多线程环境下运行是安全的。
- StringBuilder方法中没有synchronized关键字修饰,表示StringBuilder在多线程环境下运行是不安全的。
- StringBuffer是线程安全的,StringBuilder是非线程安全
StringBuilder代码演示:
public class StringBuildTest01 {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("a");
stringBuilder.append("b");
System.out.println(stringBuilder);
}
}
8种包装类
Java中为8种基本数据类型对应的准备了8种包装类型。8种包装类属于引用数据类型,父类是Object
基本数据类型 包装类型
---------------------------------------
byte java.lang.Byte(父类Number)
short java.lang.Short(父类Number)
int java.lang.Integer(父类Number)
long java.lang.Long(父类Number)
float java.lang.Float(父类Number)
double java.lang.Double(父类Number)
boolean java.lang.Boolean(父类Object)
char java.lang.Charater(父类Object)
Number类:
Number是一个抽象类,无法实例化对象。
Number类中有这样的方法:(这些方法所有的数字包装类的子类都有,这些方法是负责拆箱的)
byte byteValue()
以 byte 形式返回指定的数值。
abstract double doubleValue()
以 double 形式返回指定的数值。
abstract float floatValue()
以 float 形式返回指定的数值。
abstract int intValue()
以 int 形式返回指定的数值。
abstract long longValue()
以 long 形式返回指定的数值。
short shortValue()
以 short 形式返回指定的数值。
将基本数据类型转换为引用数据类型叫装箱
将引用数据类型转换为基本数据类型叫拆箱
代码演示:
/*
* Java中为8种基本数据类型对应的准备了8种包装类型。8种包装类属于引用数据类型,父类是Object*/
public class IntegerTest01 {
public static void main(String[] args) {
//12这是一个基本数据类型,进行构造方法包装达到了:基本数据类型向引用数据类型的转换。
//基本数据类型——(转换为)——>引用数据类型(装箱)
Integer i = new Integer(12);
//将引用数据类型——(转换为)——>基本数据类型
float f = i.floatValue();
System.out.println(f);//12.0
//将引用数据类型——(转换为)——>基本数据类型(拆箱)
int retValue = i.intValue();
System.out.println(retValue);//12
}
}
面试题:String为什么是不可变的?
我看过源代码。String类中有一个byte[]数组,这个byte[]数组采用了final修饰,因为数组一旦创建后长度不可变,并且被final修饰的引用一旦指向某个对象之后,不可再指向其它对象。所以String不可变。
面试题:StringBuffer/StringBuilder为什么是可变的?
我看过源代码,StringBuffer/StringBuilder内部实际上是一个byte[]数组,这个byte[]数组没有被final修饰,StringBuffer/StringBuilder的初始化容量为16,存满后会进行扩容,底层调用了数组拷贝的方法。System.arraycopy()…是这样扩容的。所以StringBuilder/StringBuffer适合于使用字符串的频繁拼接操作上。
Integer常用方法:
- Integer有String和int构造方法
- 除了Integer,其它的7种包装类型都有类似的构造方法
- 8种包装类都有MAX_VALUE和MIN_VALUE方法:通过访问包装类的常量,来获取该基本数据类型的最大值和最小值
代码演示(MAX_VALUE和MIN_VALUE方法):
/*
* Integer有String和int构造方法
* 除了Integer,其它的7种包装类型都有类似的构造方法
* 8种包装类都有MAX_VALUE和MIN_VALUE方法:通过访问包装类的常量,来获取该基本数据类型的最大值和最小值*/
public class IntegerTest02 {
public static void main(String[] args) {
//将数字100转换成Integer包装类型
Integer a = new Integer(100);
//将字符串"1000"转换成Integer包装类型
Integer b = new Integer("1000");
System.out.println(a + "\t" + b);//100 1000
//将double:1.314转换成Double包装类型
Double d1 = new Double(1.314);
//将字符串"5.201"转换成Double包装类型
Double d2 = new Double("5.201");
System.out.println(d1 + "\t" + d2);//1.314 5.201
System.out.println("int的最大值:" + Integer.MAX_VALUE);//int的最大值:2147483647
System.out.println("byte最小值:" + Byte.MIN_VALUE);//byte最小值:-128
//自动装箱:int类型 --自动转换为-->Integer
Integer x = 100;
}
}
整数型常量池【-128~127】
代码演示:
/*
* 自动拆箱和自动装箱只有在加减乘除运算时才会触发*/
public class IntegerTest03 {
public static void main(String[] args) {
//自动装箱:将基本数据类型转换为包装类型
Integer x = 1000;
//自动拆箱,将包装类型转换为基本数据类型,并加1.
System.out.println(x + 1);//1001
Integer y = 1000;
//"=="比较的是内存地址。
System.out.println(x==y);//false
/*例外:Integer面试题
* Java中为了提高程序的执行效率,将【-128~127】之间所有的包装对象提前创建好,
* 放到了一个方法区的“整数型常量池”中,目的时只要用这个区间的数据不需要再new了,
* 直接从整数型常量池当中取出来。
* 池:cache,就是缓存机制--大型项目中的重要手段
* */
Integer a = 127;
Integer b = 127;
//当数字在【-128~127】这个区间之内用==做比较,如果数据相同,则相等
System.out.println(a == b);//true
}
}
int,String,Integer三者之间的相互转换
代码演示:
public class IntegerTest04 {
public static void main(String[] args) {
//手动装箱
Integer x = new Integer(1000);
//手动拆箱
int y = x.intValue();
Integer a = new Integer("123");
System.out.println(a);//123
/*static int parseInt(String s)
静态方法。传参String,返回int
网页上输入的234实际上是一个String字符串类型*/
int retValue = Integer.parseInt("234");
// int retValue2 = Integer.parseInt("中文");//NumberFormatException异常
System.out.println(retValue + 100);//334
double retValue03 = Double.parseDouble("2.23");
System.out.println(retValue03 + 1.1);//3.33
//static Integer valueOf(int i) 静态方法
// 返回一个表示指定的 int 值的 Integer 实例。
//int 转换为String
//way01
int i = 100;
String s = i +"";
//String ss2 = 100 +"";//缩写版
System.out.println(s);
//way02
int i2 = 100;
String s2=String.valueOf(i);
//String ss = String.valueOf("100");//缩写版
System.out.println(s2);
//String转换为int
String s3 = "111";
int i3 = Integer.parseInt(s3);
//int ii2 = Integer.parseInt("111");//缩写版
System.out.println(i3);
//int转换为Integer 自动装箱
//int i4 =100;
//Integer in = i4;//100为int类型
Integer inn = 100;//自动装箱
System.out.println(inn);
//Integer转换为int 自动拆箱
Integer in2 = 100;//100为Integer类型
int i5 = (in2);
System.out.println(i5);
//String转换为Integer
String s4 = "100";
Integer in3 = Integer.valueOf(s4);
//Integer in5 = Integer.valueOf("100");//缩写版
System.out.println(in3);
//Integer转换为String
Integer in4 = 100;
String s5 = String.valueOf(in4);
//String s6 = String.valueOf(100);//缩写版
System.out.println(s5);
}
}
二进制,八进制,十六进制的转换
以下转换进制都是静态方法,直接类名.方法即可
static String toBinaryString(int i)
以二进制(基数 2)无符号整数形式返回一个整数参数的字符串表示形式。
static String toHexString(int i)
以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式。
static String toOctalString(int i)
以八进制(基数 8)无符号整数形式返回一个整数参数的字符串表示形式。
代码演示:
System.out.println("二进制:"+Integer.toBinaryString(20));//二进制:10100
System.out.println("十六进制:"+ Integer.toHexString(20));//十六进制:14
System.out.println("八进制:" + Integer.toOctalString(20));//八进制:24
日期类
Date类有参数表示毫秒
Date类无参表示:获取系统当前时间(精确到毫秒的系统当前时间)
日期类需要用import进行调用:import java.util.Date;
用SimpleDateFormat进行日期格式化,也需要调用:import java.text.SimpleDateFormat;返回类型为String类型
代码详解:
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* Java中对日期的处理*/
public class DateTest01 {
public static void main(String[] args) throws Exception {
//获取系统当前时间(精确到毫秒的系统当前时间)
//直接调用无参数构造方法
Date nowTime = new Date();
//java.util.Date类的toString()方法已经被重写,所以输出的是日期字符串。
System.out.println(nowTime);//Mon May 04 13:58:37 CST 2020
//日期格式化:SimpleDateFromat
//注意:在日期格式中,除了y M d H m s SS 不能随便写,其它的可以随意修改。
SimpleDateFormat sdf = new SimpleDateFormat("yyy年MM月dd日 HH:mm:ss");
String nowTimesdf = sdf.format(nowTime);
System.out.println(nowTimesdf);
//日期字符串String转换成Date类型
String time = "2020-05-04 14:19";
//注意:转换时格式必须和字符串格式一模一样,否则会出现异常
SimpleDateFormat sdf02 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date dataTime = sdf02.parse(time);
System.out.println(dataTime);
}
}
System类的相关属性和方法:
System类的相关属性和方法:
System.out[out是System类的静态变量]
System.out.prinltn()【println()方法不是System类的,是PrintStream类的方法】
System.currentTimeMillis()获取自1970年1月日到系统当前时间的总毫秒数。
System.exit(0)退出JVM
代码演示(currentTimeMillis()):
public class DateTest02 {
public static void main(String[] args) {
/* System.currentTimeMillis()无参表示:获取1970年1月1日 00:00:00到当前系统时间的总毫秒数
* 1秒 = 1000毫秒*/
long nowTimeMillis =System.currentTimeMillis();
System.out.println(nowTimeMillis);
//统计方法执行所花费的时间
long begin = System.currentTimeMillis();
price();
long end = System.currentTimeMillis();
System.out.println("花费时间为:" + (end - begin));
}
public static void price()
{
for (int i = 0; i <10000000 ; i++) {
System.out.println(i);
}
}
}
代码演示:(如何准确获取某个时间点)
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateTest03 {
public static void main(String[] args) {
//Date类有参数表示毫秒
Date time = new Date(1);//注意:参数是毫秒
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(time);
System.out.println(strTime);//1970-01-01 08:00:00 001
//获取昨天的此时时间:现在的毫秒减去一天的毫秒
Date time2 = new Date(System.currentTimeMillis() - (1000 * 60 *60 * 24));
String strTime2 = sdf.format(time2);
System.out.println(strTime2);
}
}
数字格式化(DecimalFormat)
* java.text.DecimalFormat专门负责数字格式化
* DecimalFormat df = new DecimalFormat("数字格式");
* 数字格式:
* #代表任意数字
* ,代表千分位
* .代表小数点
* 0代表不够时补0
代码演示:
import org.w3c.dom.ls.LSOutput;
import java.text.DecimalFormat;
public class DecimalFormatTest01 {
public static void main(String[] args) {
//"####,###,##"表示加入千分位,保留两位小数
DecimalFormat df = new DecimalFormat("###,###.##");
String s = df.format(1234.12312312);
System.out.println(s);//
DecimalFormat df2 = new DecimalFormat("####,###.0000");
String s2 = df2.format(12345346.456);
System.out.println(s2);
}
}
BigDecimal (常用于财务数据)
BigDecimal 属于大数据,精度极高。不属于基本数据类型,属于Java对象(引用基本数据类型)
这是SUN公司提供的一个类。专门用在财务软件当中。
java.math.BigDecimal
代码演示:
import java.math.BigDecimal;
public class BigDecimalTest01 {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal(100);
BigDecimal b2 = new BigDecimal(200);
//求和:调用方法求和:
BigDecimal b3 = b1.add(b2);
//减法:
BigDecimal b5 = b2.subtract(b1);
//乘法:
BigDecimal b6 = b1.multiply(b2);
//除法:
BigDecimal b4 = b2.divide(b1);
System.out.println("b1 + b2 =" + b3 +"\tb2 - b1 = " + b5 +"\tb1*b2 ="+b6+"\tb2/b1 =" + b4 );
//输出结果:b1 + b2 =300 b2 - b1 = 100 b1*b2 =20000 b2/b1 =2
}
}
Ramdom:(随机数)
代码演示:
import java.util.Random;
//随机数
public class RandomTest01 {
public static void main(String[] args) {
//创建随机数对象
Random random = new Random();
//随机产生一个int类型取值范围内的数字
int num01 = random.nextInt();
System.out.println(num01);
//在【1~100】区间随机生成一个int类型的数字
int num02 = random.nextInt(100);
System.out.println(num02);
}
}
练习代码:随机生成五个数,不能有相同数,如果相同则重新生成,将这五个数放在数组中
import java.util.Arrays;
import java.util.Random;
//随机数
public class RandomTest02 {
public static void main(String[] args) {
System.out.println("这五个随机数为:");
ran();
}
public static void ran(){
//创建数组:长度为5的int类型数组
int[] arr = new int[5];
//创建数组下标
int index = 0;
//创建随机数对象
Random r = new Random();
//防止数组下标0与自变量相同,给数组数据初始化值
for (int i = 0; i <arr.length ; i++) {
arr[i] = -1;
// System.out.print(arr[i]);
}
//while循环
while(index < arr.length){
//随机数的范围1~6
int a = r.nextInt(6);
//判断是否相等
if(boo(arr, a)){
arr[index++] = a;
}
}
for (int i = 0; i <arr.length ; i++) {
System.out.print(arr[i] + "\t");
}
}
public static boolean boo(int[] arr, int a){
for (int i = 0; i <arr.length ; i++) {
if (arr[i] == a){
return false;
}
}
return true;
}
}
枚举:
枚举:一枚一枚可以列出来的,才建议使用枚举类型。
枚举编译时候生成class文件。
枚举也是一种引用数据类型
枚举中的每一个值可以看做常量
语法:
enum 枚举类型名{
枚举值1,枚举值2,枚举值3...
}
结果超过两种的建议使用枚举,结果为两种的建议使用boolean
代码演示:
/*
例如:
* */
public class EnumTest01 {
public static void main(String[] args) {
enum_ee();
}
public static Seanson enum_ee(){
return Seanson.SPRING;
}
}
enum Seanson{
//春夏秋冬
SPRING,SUMMER,AUTUMN,WINTER
}
java异常处理机制
编译时异常与运行时异常
Object下面有Thrwoable(可抛出的)
Throwoable下面有两个分支:Error(不可处理的,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常:要求程序员在编写程序阶段必须预先对这些异常进行处理,否则编译器报错。
RuntimeException:运行时异常:在编写程序阶段程序员可以预先处理,也可以不管。
编译时异常又称:受检异常( CheckedException),受控异常
运行时异常又称:未受检异常(UncheckedEception),非受控异常
编译时异常与运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。
编译时异常因为编译时异常必须在编译(编写)阶段预先处理,如果不处理,编译器报错,从而起名编译异常。
所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象,而异常的发生就是new异常对象。
编译时异常和运行时异常区别:
编译时异常发生概率比较高。
运行时异常一般发生的概率比较低。
Java语言中对异常的处理包括两种方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
第二种方式:使用try...catch语句进行异常捕捉。
注意:只要异常没有捕捉,采用上抛的方式,此方法的后续代码不执行。
在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选?
如果希望调用者来处理,选择throws上报。
其它情况使用捕捉的方式。
一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM。JVM只有终止。
异常处理机制的作用就是增强程序的健壮性。怎么能做到,异常发生了也不影响程序的执行。所以
一般main方法中的异常建议使用try..catch进行捕捉。main就不要继续上抛了。
代码演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest06 {
// 一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM。JVM只有终止。
// 异常处理机制的作用就是增强程序的健壮性。怎么能做到,异常发生了也不影响程序的执行。所以
// 一般main方法中的异常建议使用try..catch进行捕捉。main就不要继续上抛了。
/*
public static void main(String[] args) throws FileNotFoundException {
System.out.println("main begin");
m1();
System.out.println("main over");
}
*/
public static void main(String[] args) {
// 100 / 0这是算术异常,这个异常是运行时异常,你在编译阶段,可以处理,也可以不处理。编译器不管。
//System.out.println(100 / 0); // 不处理编译器也不管
// 你处理也可以。
/*
try {
System.out.println(100 / 0);
} catch(ArithmeticException e){
System.out.println("算术异常了!!!!");
}
*/
System.out.println("main begin");
try {
// try尝试
m1();
// 以上代码出现异常,直接进入catch语句块中执行。
System.out.println("hello world!");
} catch (FileNotFoundException e){ // catch后面的好像一个方法的形参。
// 这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址。
// catch是捕捉异常之后走的分支。
// 在catch分支中干什么?处理异常。
System.out.println("文件不存在,可能路径错误,也可能该文件被删除了!");
System.out.println(e); //java.io.FileNotFoundException: D:\course\01-课\学习方法.txt (系统找不到指定的路径。)
}
// try..catch把异常抓住之后,这里的代码会继续执行。
System.out.println("main over");
}
private static void m1() throws FileNotFoundException {
System.out.println("m1 begin");
m2();
// 以上代码出异常,这里是无法执行的。
System.out.println("m1 over");
}
// 抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理
//private static void m2() throws ClassCastException{
// 抛FileNotFoundException的父对象IOException,这样是可以的。因为IOException包括FileNotFoundException
//private static void m2() throws IOException {
// 这样也可以,因为Exception包括所有的异常。
//private static void m2() throws Exception{
// throws后面也可以写多个异常,可以使用逗号隔开。
//private static void m2() throws ClassCastException, FileNotFoundException{
private static void m2() throws FileNotFoundException {
System.out.println("m2 begin");
// 编译器报错原因是:m3()方法声明位置上有:throws FileNotFoundException
// 我们在这里调用m3()没有对异常进行预处理,所以编译报错。
// m3();
m3();
// 以上如果出现异常,这里是无法执行的!
System.out.println("m2 over");
}
private static void m3() throws FileNotFoundException {
// 调用SUN jdk中某个类的构造方法。
// 这个类还没有接触过,后期IO流的时候就知道了。
// 我们只是借助这个类学习一下异常处理机制。
// 创建一个输入流对象,该流指向一个文件。
/*
编译报错的原因是什么?
第一:这里调用了一个构造方法:FileInputStream(String name)
第二:这个构造方法的声明位置上有:throws FileNotFoundException
第三:通过类的继承结构看到:FileNotFoundException父类是IOException,IOException的父类是Exception,
最终得知,FileNotFoundException是编译时异常。
错误原因?编译时异常要求程序员编写程序阶段必须对它进行处理,不处理编译器就报错。
*/
//new FileInputStream("D:\\course\\01-开课\\学习方法.txt");
// 我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛。
// 一个方法体当中的代码出现异常之后,如果上报的话,此方法结束。
new FileInputStream("D:\\course\\01-课\\学习方法.txt");
System.out.println("如果以上代码出异常,这里会执行吗??????????????????不会!!!");
}
}
try…catch
try语句块中的某一行出现异常,该行后面的代码不会执行。
try...catch捕捉异常之后,后续代码可以执行。
catch后面的小括号中的类型可以时具体的异常类型,也可以是该异常类型的父类型。
catch可以写多个。建议catch的时候,精确的一个个处理,这样有利于程序调试
catch写多个时,必须从上到下,从小到大。
代码演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
深入try..catch
1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
2、catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
3、catch写多个的时候,从上到下,必须遵守从小到大。
*/
public class ExceptionTest07 {
/*
public static void main(String[] args) throws Exception, FileNotFoundException, NullPointerException {
}
*/
/*public static void main(String[] args) throws Exception {
}*/
public static void main(String[] args) {
//编译报错
/*try {
FileInputStream fis = new FileInputStream("D:\\course\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
} catch(NullPointerException e) {
}*/
/*try {
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
System.out.println("以上出现异常,这里无法执行!");
} catch(FileNotFoundException e) {
System.out.println("文件不存在!");
}
System.out.println("hello world!");*/
/*try {
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
} catch(IOException e) { // 多态:IOException e = new FileNotFoundException();
System.out.println("文件不存在!");
}*/
/*try {
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
} catch(Exception e) { // 多态:Exception e = new FileNotFoundException();
System.out.println("文件不存在!");
}*/
/*try {
//创建输入流
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
//读文件
fis.read();
} catch(Exception e) { //所有的异常都走这个分支。
System.out.println("文件不存在!");
}*/
/*try {
//创建输入流
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
//读文件
fis.read();
} catch(FileNotFoundException e) {
System.out.println("文件不存在!");
} catch(IOException e){
System.out.println("读文件报错了!");
}*/
// 编译报错。
/*
try {
//创建输入流
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
//读文件
fis.read();
} catch(IOException e){
System.out.println("读文件报错了!");
} catch(FileNotFoundException e) {
System.out.println("文件不存在!");
}
*/
// JDK8的新特性!
try {
//创建输入流
FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
// 进行数学运算
System.out.println(100 / 0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
} catch(FileNotFoundException | ArithmeticException | NullPointerException e) {
System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
}
}
}
异常对象重要的两个方法:(getMessage(),printStackTrace())
获取异常简单的描述信息:
String msg = exception.getMessage();
打印异常追踪的堆栈信息:
exception.printStackTrace();(常用)
我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢?
异常信息追踪信息,从上往下一行一行看。
但是需要注意的是:SUN写的代码就不用看了(看包名就知道是自己的还是SUN的。)。
主要的问题是出现在自己编写的代码上。
代码演示:
public class ExceptionTest08 {
public static void main(String[] args) {
// 这里只是为了测试getMessage()方法和printStackTrace()方法。
// 这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
NullPointerException e = new NullPointerException("空指针异常fdsafdsafdsafds");
// 获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。
String msg = e.getMessage(); //空指针异常fdsafdsafdsafds
System.out.println(msg);
// 打印异常堆栈信息
// java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
e.printStackTrace();
for(int i = 0; i < 1000; i++){
System.out.println("i = " + i);
}
System.out.println("Hello World!");
}
}
final子句(关键字):
关于try..catch中的finally子句:
1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
finally子句必须和try一起出现,不能单独编写。
2、finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。
即使try语句块中的代码出现异常,finally中代码也会正常执行。
3,try和finally,没有catch可以吗?可以。
try不能单独使用。
try finally可以联合使用。
4,以下代码的执行顺序:
先执行try...
再执行finally...
最后执行 return (return语句只要执行方法必然结束。)
代码演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest10 {
public static void main(String[] args) {
FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。
try {
// 创建输入流对象
fis = new FileInputStream("D:\\course\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
// 开始读文件....
String s = null;
// 这里一定会出现空指针异常!
s.toString();
System.out.println("hello world!");
// 流使用完需要关闭,因为流是占用资源的。
// 即使以上程序出现异常,流也必须要关闭!
// 放在这里有可能流关不了。
//fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
} catch(NullPointerException e) {
e.printStackTrace();
} finally {
System.out.println("hello 浩克!");
// 流的关闭放在这里比较保险。
// finally中的代码是一定会执行的。
// 即使try中出现了异常!
if (fis != null) { // 避免空指针异常!
try {
// close()方法有异常,采用捕捉的方式。
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("hello kitty!");
}
}
fanal面试题
代码演示:
/*
finally面试题
*/
public class ExceptionTest13 {
public static void main(String[] args) {
int result = m();
System.out.println(result); //100
}
/*
java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!):
java中有一条这样的规则:
方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
java中海油一条语法规则:
return语句一旦执行,整个方法必须结束(亘古不变的语法!)
*/
public static int m(){
int i = 100;
try {
// 这行代码出现在int i = 100;的下面,所以最终结果必须是返回100
// return语句还必须保证是最后执行的。一旦执行,整个方法结束。
return i;
} finally {
i++;
}
}
}
/*
反编译之后的效果
public static int m(){
int i = 100;
int j = i;
i++;
return j;
}
*/
final,finally,finalize的区别
final 关键字
final修饰的类无法继承
final修饰的方法无法覆盖
final修饰的变量不能重新赋值。
finally 关键字
和try一起联合使用。
finally语句块中的代码是必须执行的。
finalize 标识符
是一个Object类中的方法名。
这个方法是由垃圾回收器GC负责调用的。
代码演示:
public class ExceptionTest14 {
public static void main(String[] args) {
// final是一个关键字。表示最终的。不变的。
final int i = 100;
//i = 200;
// finally也是一个关键字,和try联合使用,使用在异常处理机制中
// 在fianlly语句块中的代码是一定会执行的。
try {
} finally {
System.out.println("finally....");
}
// finalize()是Object类中的一个方法。作为方法名出现。
// 所以finalize是标识符。
// finalize()方法是JVM的GC垃圾回收器负责调用。
Object obj;
}
}
// final修饰的类无法继承
final class A {
// 常量。
public static final double MATH_PI = 3.1415926;
}
class B {
// final修饰的方法无法覆盖
public final void doSome(){
}
}
自定义异常类
第一步:编写一个类继承Exception或者RuntimeException.
第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
代码演示:
public class MyException extends Exception{ // 编译时异常
public MyException(){
}
public MyException(String s){
super(s);
}
}
/*
public class MyException extends RuntimeException{ // 运行时异常
}
*/
综合训练
题目:
开放型题目,随意发挥:
写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器),
该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有的最大武器数量,
并用这一大小来初始化数组w。
该类还提供一个方法addWeapon(Weapon wa),表示把参数wa所代表的武器加入到数组w中。
在这个类中还定义两个方法attackAll()让w数组中的所有武器攻击;
以及moveAll()让w数组中的所有可移动的武器移动。
写一个主方法去测试以上程序。
提示:
Weapon是一个父类。应该有很多子武器。
这些子武器应该有一些是可移动的,有一些
是可攻击的。
代码演示:
package com.bjpowernode.javase.array;
public class HomeWork03 {
public static void main(String[] args) {
Army army = new Army(5);
//多态:父类型引用指向子类型对象
Weapon weapon1 = new Tank();
Weapon weapon2 = new yunshuji();
Weapon weapon3 = new buqiang();
try {
army.addWeapon(weapon1);
army.addWeapon(weapon2);
army.addWeapon(weapon3);
}catch (myException m){
System.out.println(m.getMessage());
}
army.moveAll();
army.attackAll();
}
}
//创建一个Army类代表军队
class Army {
//创建一个Weapon属性的数组w
Weapon[] w;
//通过构造方法限定Army类所能拥有的最大化武器数量,同时初始化w。
public Army(int count) {
w = new Weapon[count];
}
//addWeapon(Weapon wa)方法将wa和数组相关联
public void addWeapon(Weapon wa) throws myException{
for (int i = 0; i <w.length ; i++) {
if(w[i] == null){
w[i] = wa;
System.out.println(wa + "武器添加成功");
return;
}
}
throw new myException("警告!能源耗尽!");
}
//定义attackAll()方法,遍历w攻击。
public void attackAll(){
for (int i = 0; i <w.length ; i++) {
if (w[i] instanceof Attack){
Attack attack = (Attack)w[i];
attack.attack();
}
}
}
//定义moveAll()方法,遍历w移动
public void moveAll(){
for (int i = 0; i <w.length ; i++) {
if(w[i] instanceof Moveable){
Moveable moveable = (Moveable)w[i];
moveable.move();
}
}
}
}
//定义一个接口
interface Moveable{
void move();
}
interface Attack{
void attack();
}
class Weapon{
}
class Tank extends Weapon implements Moveable,Attack{
@Override
public void move() {
System.out.println("坦克移动");
}
@Override
public void attack() {
System.out.println("坦克攻击");
}
@Override
public String toString() {
return "坦克";
}
}
class yunshuji extends Weapon implements Moveable{
@Override
public void move() {
System.out.println("运输机移动");
}
//重写toString
public String toString(){
return "运输机";
}
}
class buqiang extends Weapon implements Attack{
@Override
public void attack() {
System.out.println("步枪攻击");
}
//重写toString方法
public String toString()
{
return "步枪";
}
}
class myException extends Exception{
public myException() {
}
public myException(String message) {
super(message);
}
}
转载:https://blog.csdn.net/weixin_45144691/article/details/105599288