文章目录
01.final 再次总结:
final修饰的变量?
final修饰的局部变量,一旦赋值不能重新赋值。
public class Main {
public static void main(String[] args) {
final int a=100;
System.out.println(a);//100
//final修饰的局部变量只能赋值一次,无法重新赋值
/*a=200;
System.out.println(a);
*/
final Animal anm=new Animal(999);
System.out.println(anm.getVal());//999
//final修饰的引用,无法更改引用方向,但可以修改数据的值
/*
anm = new Animal();
System.out.println(anm.getVal());
final Animal anm=new Animal(999);
*/
anm.setVal(1000);
System.out.println(anm.getVal());//1000
}
}
class Animal{
private int val;
//构造方法
public Animal(){}
public Animal(int val){
this.val=val;
}
//setter和getter
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
}
final修饰的方法?
final修饰的方法无法被覆盖,被重写。
例子:
public class Main {
public static void main(String[] args) {
Animal anm=new Cat();
anm.dosome();
}
}
class Animal{
//final修饰的方法无法被覆盖,被重写。
/*
public final void dosome(){
System.out.println("动物");
}
*/
public void dosome(){
System.out.println("动物");
}
}
class Cat extends Animal{
public void dosome(){
System.out.println("猫");
}
}
final修饰的类?
final修饰的类无法继承。
public class Main {
public static void main(String[] args) {
Animal anm=new Cat();
anm.dosome();
}
}
//final修饰的类,无法继承
/*
class final Animal{
public void dosome(){
System.out.println("动物");
}
}
*/
class Animal{
public void dosome(){
System.out.println("动物");
}
}
class Cat extends Animal{
public void dosome(){
System.out.println("猫");
}
}
总结:final修饰的变量,只能赋一次值。
02.抽象类
1.什么是抽象类?
1)类与类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。
2)类本身是不存在的,所以抽象类无法创建对象,即无法实例化。
2.抽象类输入什么类型?
抽象类也属于引用数据类型
3.抽象类怎么定义?
语法:
[修饰符列表] abstract class 类名{
类体;
}
4.抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。
5.final和abstract不能联合使用,这两个关键字是对立的。
6.抽象类的子类可以是抽象类。
7.抽象类虽无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的。
8.抽象类中不一定有抽象方法,抽象方法一定在抽象类中。
9.抽象方法的定义?
public abstract void dosome();
抽象方法的特点:
1)没有方法体,以分号结尾
2)前面修饰符列表中有abstract关键字
10.一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现
例子:
public class Main {
public static void main(String[] args) {
//抽象类是无法实例化的,无法创建对象,所以抽象类是用来被子类继承的
/*
Animal anm=new Animal();
*/
Animal c=new Cat();
c.dosome();
}
}
//final和abstract不能共存,两个关键字对立关系
//java: 非法的修饰符组合: abstract和final
/*
final abstract class Animal{
public abstract void dosome();
}
*/
//抽象类的定义
abstract class Animal{
//抽象类虽无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的
public Animal(){
System.out.println("抽象类");
}
//抽象方法的定义
//抽象类中不一定有抽象方法,抽象方法一定在抽象类中
public abstract void dosome();
}
//继承
class Cat extends Animal{
public Cat(){
super();
System.out.println("继承抽象类的Cat类的构造方法");
}
//继承抽象类的时候,要进行方法覆盖
public void dosome(){
System.out.println("覆盖抽象类的方法");
}
}
//抽象类的子类可以是抽象类
/*
abstract class Dog extends Animal{
public abstract void dosome();
}
*/
03.接口
接口的基础语法:
1.接口也是一种"引用数据类型"。
2.接口是完全抽象的。(抽象类是半抽象的。)或者也可以说接口是特殊的抽象类。
3.接口怎么定义:
[修饰符列表] interface 接口名{}
4.接口支持多继承,一个接口可以继承多个接口。
5.接口中只有常量+抽象方法。
6.接口中所有的元素都是public修饰的。
7.接口中抽象方法的public abstract可以省略。
8.接口中常量的public static final可以省略。
9.接口中方法不能有方法体。
10.一个非抽象的类,实现接口的时候,必须将接口中所有的方法加以实现。
11.一个类可以实现多个接口。
12.extends和implements可以共存,extends在前,implements在后
13.使用接口,写代码的时候,可以使用多态(父类型引用指向子类型的对象)
样例1:接口中只有常量+抽象方法
public class Main {
public static void main(String[] args) {
//访问接口的常量
//System.out.println(MyMath.PI);//3.1415926
//常量能重新赋值么?
//错误:无法为最终变量PI分配值
//MyMath.PI=3.1415928;
}
}
//定义接口
interface A{
}
//接口支持继承
interface B extends A{
}
//接口支持多继承,一个接口可以继承多个接口
interface C extends A,B{
}
//我的数学接口
interface MyMath{
//常量
//public static final double PI=3.1415926;
double PI=3.1415926;
//抽象方法
//public abstract int sum(int a,int b);
//接口当中既然都是抽象方法,那么在编写代码的时候,public abstract可以省略吗?可以
int sum(int a,int b);
//接口中的方法可以带有方法体吗?不可以
//错误:接口抽象方法不能带有主体
/*
void doSome(){
}
*/
//相减的抽象方法
int sub(int a,int b);
}
样例2:当一个非抽象的类实现接口的话,必须将抽象方法全部实现(覆盖、重写)
public class Main {
public static void main(String[] args) {
//错误:MyMath是抽象的; 无法实例化
//new MyMath();
//能使用多态吗?
//Animal anm=new Cat();
//父类型的引用指向子类型的对象
MyMath a=new MyMathImpl();
//调用接口里面的方法(面向接口编程)
//编译时,静态绑定接口内的抽象方法
//运行时,动态绑定子类的方法
System.out.println(a.sum(2,1));//3
System.out.println(a.sub(2,1));//1
}
}
/*
接口的基础语法:
1.类和类之间叫做继承,类和接口之间叫做实现。
但是:仍然可以将实现看作"继承"。
继承使用extends关键词完成。
实现使用implements关键字完成。
2.当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(重写/覆盖)。
*/
//特殊的抽象类,完全抽象的,叫做接口。
interface MyMath{
//常量
//public static final已省略
double PI=3.1415926;
//抽象方法
//public abstract已省略
int sum(int a,int b);
int sub(int a,int b);
}
//这样没问题
/*
abstract class MyMathImpl implements MyMath{
}
*/
//编写一个类(这个类是一个非抽象的类)
//错误:MyMathImpl不是抽象的, 并且未覆盖MyMath中的抽象方法sub(int,int)
/*
class MyMathImpl implements MyMath{
}
*/
//修正
class MyMathImpl implements MyMath{
//错误:正在尝试分配更低的访问权限; 以前为public
//访问权限只能更高,不能更低:private<无参<protected<public
/*
int sum(int a,int b){
return a+b;
}
*/
//重写/覆盖/实现 接口中的方法(通常叫做实现。)
public int sum(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
样例3:类可以实现多个接口
/*
1.接口和接口之间支持多继承,那么一个类可以同时实现多个接口吗?
对于计算机来说,一个机箱上有多个接口,一个接口是接键盘的,
一个接口是接鼠标的,一个接口是接电源的,一个接口是接显示器的.....
2.重点:一个类可以同时实现多个接口。
这种机制弥补了Java中的哪个缺陷?
Java中类和类只支持单继承,实际上单继承是为了简单而出现的,现实世界中
存在多继承,Java中的接口弥补了单继承带来的缺陷。
3.接口A和接口B虽然没有继承关系,但是写代码的时候,可以互转。
编译器没意见。但是运行时可能出现:ClassCastException
4.之前有一个结论:
无论向上转型还是向下转型,两种类型之间必须有继承关系,
没有继承关系编译器会报错。(这句话不使用在接口方面)
最终实际上和之前一样,需要加:instance运算符进行判断
向下转型养成好习惯。转型之前先if+instance进行判断。
*/
public class Main {
public static void main(String[] args) {
//多态怎么用呢?
//都是父类型引用指向子类型对象
A a=new D();
//a.m2();//编译报错。A接口中没有m2()方法.
B b=new D();
C c=new D();
//这个编译没问题,运行也没问题
//调用其他接口中的个方法,你需要转型(接口转型)
B b1=(B)a;
b1.m2();//m2....
//直接向下转型为D可以吗?可以
D d=(D)a;
d.m2();//m2....
M m=new E();
//经过测试:接口和接口之间再进行强制转换的时候,没有继承关系,也可以强转。
//但是一定要注意,运行时可能会出现ClassCastException异常。
//编译没问题,运行有问题
//K k=(K)m;
if(m instanceof K){
K k=(K)m;
}
}
}
interface K{
}
interface M{
}
class E implements M{
}
//-----------------------------------------------------
interface X{
}
interface Y{
}
interface Z extends X,Y{//接口和接口支持多继承
}
//-----------------------------------------------------
interface A{
void m1();
}
interface B{
void m2();
}
interface C{
void m3();
}
//实现多个接口,其实就类似于多继承。
class D implements A,B,C{
//实现A接口的m1()
public void m1(){
}
//实现B接口的m2()
public void m2(){
System.out.println("m2....");
}
//实现C接口的m3()
public void m3(){
}
}
样例4:extends和implements可以共存,extends在前,implements在后
/*
继承和实现都存在的话,代码应该怎么写?
extends 关键字在前
implements 关键词在后
*/
public class Main {
public static void main(String[] args) {
//创建对象
Flyable f=new Cat();//多态
f.fly();//飞猫起飞,翱翔太空
//同一个接口
Flyable f2=new Pig();
//调用同一个fly方法,最后的执行效果不同
f2.fly();//我是一只会飞的猪猪
Flyable f3=new Fish();
f3.fly();//努力的飞鱼
}
}
//动物类:父类
class Animal{
}
//可飞翔的接口(是一对翅膀)
//能插拔的就是接口。(没有接口你怎么插拔)
//内存条插到主板上,他们之间有接口。内存条可以更换。
//接口通常提取的是行为动作.
interface Flyable{
void fly();
}
//动物类子类:猫类
// Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变得可以飞翔。
class Cat extends Animal implements Flyable{
public void fly(){
System.out.println("飞猫起飞,翱翔太空");
}
}
//蛇类,如果你不想让它非,可以不是先Flyable接口
//没有实现这个接口表示你没有翅膀,没有给你翅膀,你肯定不能飞
class Snack extends Animal{
}
//想飞就插翅膀这个接口
class Pig extends Animal implements Flyable{
public void fly(){
System.out.println("我是一只会飞的猪猪");
}
}
//鱼(默认实际上是存在继承的,默认继承Object)
/*
class Fish extends Object implements Flyable{
}
*/
class Fish implements Flyable{//没写extends,也是有的,默认继承Object+
public void fly(){
System.out.println("努力的飞鱼");
}
}
接口在开发中的作用
1.注意:接口在开发中的作用,类似于多态在开发中的作用
2.多态:面向对象编程,不要面向具体编程。降低程序的耦合度,提高程序的扩展力。
/*
public class Master{
public void feed(Dog d){
}
public void feed(Cat c){
}
//假设又要养其他的宠物,那么这个时候需要再加1个方法。
//这样扩展力太差了,违背了OCP原则(对扩展开发,对修改关闭。)
}
*/
public class Master{
public void feed(Animal a){
//面向Animal父类编程,父类比子类更抽象。
//所以我们叫做面向抽象编程,不要面向具体编程
//这样程序的扩展力就强
}
}
3.接口在开发中的作用?
接口是不是完全的?是。
而我们以后正好要求,面向抽象编程。
面向抽象编程这句话以后可以修改为:面向接口编程。
有了接口就有了可插拔。可插拔表示扩展里很强。不是焊接死的。
主板和内存条之间有插槽,这个插槽就是接口,内存条坏了,可以重新
买一个换下来。这叫做高扩展性。(低耦合度。)
接口在现实世界中是不是到处都是呢?
usb接口
螺栓和螺母之间有接口
灯泡和灯口之间有接口
笔记本电脑和键盘之间有接口(usb接口,usb接口是不是某个计算机协会制定的规范)
接口有什么用?扩展性好,可插拔。
接口是一个抽象的概念。
分析:
中午要去饭馆吃饭,这个过程有接口吗?
接口是抽象的。
菜单是一个接口(菜单上有一个抽象的照片:西红柿炒鸡蛋)
谁面向接口调用。(顾客面向菜单点菜,调用接口。)
谁负责实现这个接口。(后台厨师负责把西红柿炒鸡蛋做好!是接口的实现者)
这个接口有什么用呢?
这个饭馆的菜单,让"顾客"和"后厨"解耦合了。
顾客不用找后厨,后厨不用找顾客,他们之间完全依靠这个抽象的菜单沟通。
总结一句话:三个字"解耦合"
1)面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP的开发原则。
2)接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)
3)接口可以解耦合,解开的是谁和谁的耦合?
任何一个接口都由调用者和实现者。(顾客、厨师)
接口可以将调用者和实现者解耦合。
调用者面向接口调用。
实现者面向接口编写实现。
例子:顾客与厨师之间通过接口"解耦合"
Main:--------------------
public class Main {
public static void main(String[] args) {
//创建厨师对象
//FoodMenu cooker1=new ChinaCooker();
FoodMenu cooker1=new AmericCooker();
//创建顾客对象
Customer customer=new Customer(cooker1);
//顾客点菜
customer.order();
}
}
FoodMenu:----------------
/*
接口:菜单,抽象的
*/
public interface FoodMenu {
//西红柿炒鸡蛋
void shiZiChaoJiDan();
//鱼香肉丝
void yuXiangRouSi();
}
Customer:-----------------------
//顾客
//调用菜单上的菜
//顾客是接口的调用者
public class Customer {
//顾客手里有一个菜单
//Customer has a FoodMenu!(意思:顾客有一个菜单)
//记住:以后凡是能够使用 has a 来描述的,统一以属性的方式存在。
//实例变量,属性
//面向抽象编程,面向接口编程。降低程序的耦合性,提高程序的扩展力。
private FoodMenu foodMenu;//都要养成封装的好习惯
//如果以下这样写,就表示写死了(焊接了---没有可插拔了)
//中餐厨师
//ChinaCooker ac;
//西餐厨师
//AmericCooker ac;
//构造方法
public Customer(){
}
public Customer(FoodMenu foodMenu) {
this.foodMenu = foodMenu;
}
//setter and getter
public FoodMenu getFoodMenu() {
return foodMenu;
}
public void setFoodMenu(FoodMenu foodMenu) {
this.foodMenu = foodMenu;
}
//提供一个点菜的方法
public void order(){
//先拿到菜单才能点菜
//调用get方法拿菜单
//FoodMenu fm=this.getFoodMenu();
//也可以不调用get方法拿菜单,因为在本类中私有的属性可以访问
foodMenu.shiZiChaoJiDan();
foodMenu.yuXiangRouSi();
}
}
/*
Cat is a Animal, 但凡满足 is a 的都表示可以设置为继承
Customer has a FoodMenu, 但凡满足 has a 的都表示以属性的形式存在
*/
ChinaCooker:-------------------------------
//中餐厨师
//实现菜单上的菜
//厨师是接口的实现者
public class ChinaCooker implements FoodMenu{
//西红柿炒鸡蛋
public void shiZiChaoJiDan(){
System.out.println("中餐厨师做的西红柿炒鸡蛋!");
}
//鱼香肉丝
public void yuXiangRouSi(){
System.out.println("中餐厨师做的鱼香肉丝!");
}
}
AmericCooker:------------------------------
//西餐厨师
//实现菜单上的菜
//厨师是接口的实现者
public class AmericCooker implements FoodMenu{
//西红柿炒鸡蛋
public void shiZiChaoJiDan(){
System.out.println("西餐厨师做的西红柿炒鸡蛋!");
}
//鱼香肉丝
public void yuXiangRouSi(){
System.out.println("西餐厨师做的鱼香肉丝!");
}
}
04.类型和类型之间的关系
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:
Coker like a FoodMenu(厨师像一个菜单一样)
凡是能够满足like a关系的表示"实现关系"
实现关系通常是:类实现接口。
A implements B
05.抽象类和接口的区别
在这里我们只说一下抽象类和接口在语法上的区别。
至于以后抽象类和接口应该怎么进行选择,通过后面的项目去体会/学习。
抽象类是半抽象的。
接口是完全抽象的。
抽象类中有构造方法。
接口中没有构造方法。
类和类直间只能单继承。
接口和接口之间支持多继承。
一个类可以同时实现多个接口。
一个抽象类只能继承一个类。(单继承)
接口中只允许出现常量和抽象方法。
小道消息:
以后接口使用的比抽象类多。一般抽象类使用的还是少。
接口一般都是对"行为"的抽象。
06.package和import
1.package:
1)包名命名规范:
公司域名倒叙 + 项目名 + 模块名 + 功能名
2)package出现在Java源文件第一行
3)带有包名怎么编译?
javac -d . xxx.java
4)怎么运行?
java 完整类名
补充:以后说类名的时候,如果带着包名描述,表示完整类名。
如果没有带包,描述的话,表示简类名。
java.util.Scanner 完整类名。
Scanner 简类名
2.import:
1)import什么时候不需要?
java.lang不需要
同包下不需要
其他一律需要
2)怎么用?
import 完整类名;
import 包名.*;
import java.util.Scanner;//完整类名
improt java.util.*;//编译器编译的时候,会自动把*变成具体的类名
07.访问控制权限
访问控制修饰符 本类 同包 子类 任意位置
----------------------------------------------------------------------------
public 可以 可以 可以 可以
protected 可以 可以 可以 不可以
默认 可以 可以 不可以 不可以
private 可以 不可以 不可以 不可以
----------------------------------------------------------------------------
1.范围从大到小排序:public > protected > 默认 > private
2.访问控制权限修饰符可以修饰什么?
属性(4个都能用)
方法(4个都能用)
类(public和默认能用,其他不行)
接口(public和默认能用,其他不行)
…
08.Object类库
1.这个老祖宗类中的方法需要研究一下,因为这些方法都是所有子类通用的。任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。
2.Object类当中有哪些常用方法?
1)如何查找?
第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
第二种方法:去查阅Java的类库的帮助文档。
2)什么是API?
应用程序编程接口。(Application Program Interface)
整个JDK的类库就是一个Javase的API。
每一个API都会配置一套API帮助文档。
SUN公司提前写好的这套类库就是API。
3)目前为止只需要掌握这几个方法即可:
protected Object clone() //负责对象克隆的
int hashCode() //获取对象哈希值的一个方法
boolean equals(Object obj) //判断两个对象是否相等
String toString() //将对象转换成字符串形式
protected void finalize() //垃圾回收器负责调用的方法
关于Object类中的toString()方法:
源代码长什么样?
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
源代码上toString()方法的默认实现是:
类名@对象的内存地址转换为十六进制的形式
2.SUN公司设计toString()方法的目的是什么?即toString()的作用是什么?
toStringk()方法的设计目的是:通过调用这个方法可以将一个"Java对象"转换成"字符串表示形式"
3.其实SUN公司开发Java语言的时候,建议所有的子类都去重写toString()方法。
toString()方法应该是一个简洁的、详实的、已阅读的。
样例:
public class Main {
public static void main(String[] args) {
MyTime t=new MyTime(1970,1,1);
String s=t.toString();
System.out.println(s);//1970年1月1日
//注意:输出引用的时候,会自动调用该引用的toString()方法
System.out.println(t);
}
}
class MyTime{
private int year;
private int month;
private int day;
public MyTime(){}
public MyTime(int year,int month,int day){
this.year=year;
this.month=month;
this.day=day;
}
//若没有对toString进行重写,输出结果为:MyTime@4eec7777
//重写后,输出结果为:1970年1月1日
public String toString(){
return year+"年"+month+"月"+day+"日";
}
}
关于Object类中的equals方法:
1)equals方法的源代码
public boolean equals(Object obj) {
return (this == obj);
}
以上这个方法是Object类的默认实现
2)SUN公司设计equals方法的目的是什么?
以后编程的过程当中,都需要通过equals方法来判断两个对象是否相等。
equals方法是判断两个对象是否相等的。
3)我们需要研究一下Object类给的这个默认的equals方法够不够用!
在Object类中的equals方法当中,默认采用的是"“判断两个Java对象是否相等。而”“判断的是两个Java对象的内存地址,我们应该判断两个Java对象的内容是否相等,所以老祖宗的equals方法不够用,需要子类重写equals
4)判断两个Java对象是否相等,不能使用”“,因为”"比较的是两个对象的内存地址。
例子:
public class Main {
public static void main(String[] args) {
//判断两个基本数据类型的数据是否相等直接使用"=="就行
int m=100;
int n=100;
//这个"=="是判断a中保存的100和b中保存的100是否相等。
System.out.println(m==n);//true(相等),false(不相等)
//判断两个Java对象是否相等,我们怎么办?能直接用"=="吗?
//创建一个日期对象是:2008年8月8日
MyTime t1=new MyTime(2008,8,8);//MyTime t1 = 0x1234;
//创建了一个新的日期对象,也是2008年8月8日
MyTime t2=new MyTime(2008,8,8);//MyTime t2 = 0x5210;
//测试一下,比较两个对象是否相等,能不能用"=="???
//这里的"=="判断的是t1中保存的对象内存地址和t2中保存的对象内存地址是否相等。
System.out.println(t1==t2);//false
//重写Obejct equals方法之前(比较的是内存地址)
/*
boolean b=t1.equals(t2);
System.out.println(b);//flase
*/
//重写Obejct equals方法之后(比较的是内容)
System.out.println(t1.equals(t2));//true
//再创建一个新的日期
MyTime t3=new MyTime(2008,8,9);
System.out.println(t1.equals(t3));//false
//我们这个程序有bug吗?可以运行,但是效率低,怎么改造?
MyTime t4=null;
System.out.println(t1.equals(t4));//false
}
}
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;
}
//默认的equals方法
/*
public boolean equals(Object obj) {
return (this == obj);
}
*/
/*//重写Object类的equals方法
//怎么重写?复制粘贴。相同的返回值类型、相同的方法名、相同的形式参数列表
//equals到底怎么重写?你认为两个对象什么相等的时候表示相等,就怎么写!
public boolean equals(Object obj) {
//当年相同,月相同,日相同,表示两个日期相同。两个对象相等
//获取第一个日期的年月日
//获取第二个日期的年月日
//开始比对
int year1=this.year;
int month1=this.month;
int day1=this.day;
if(obj instanceof MyTime){
MyTime tmp=(MyTime) obj;//向下转型,要强制转换
int year2=tmp.year;
int month2=tmp.month;
int day2=tmp.day;
if(year1==year2&&month1==month2&&day1==day2) return true;
}
//程序能执行到此处,表示日期不相等
return false;
}*/
/*//改良之后的equals方法
public boolean equals(Object obj) {
//如果obj为空,直接返回false
if(obj==null){
return false;
}
//如果obj不是一个MyTime类型,没必要比较,直接返回false
if(!(obj instanceof MyTime)){
return false;
}
//如果this和obj保存的内存地址相同,没必要比较,直接返回true
//内存地址相同的时候指向的对内存的对象肯定是同一个
if(this==obj){
return true;
}
//程序能执行到此处说明了什么?
//说明obj不是null,obj是MyTime类型
MyTime tmp=(MyTime)obj;
if(this.year==tmp.year&&this.month==tmp.month&&this.day==tmp.day){
return true;
}
//程序能这里直接返回false
return false;
}*/
/*//再次改良
public boolean equals(Object obj) {
//如果obj为空,直接返回false
if(obj==null){
return false;
}
//如果obj不是一个MyTime类型,没必要比较,直接返回false
if(!(obj instanceof MyTime)){
return false;
}
//如果this和obj保存的内存地址相同,没必要比较,直接返回true
//内存地址相同的时候指向的对内存的对象肯定是同一个
if(this==obj){
return true;
}
//程序能执行到此处说明了什么?
//说明obj不是null,obj是MyTime类型
MyTime tmp=(MyTime)obj;
return this.year==tmp.year&&this.month==tmp.month&&this.day==tmp.day;
}*/
//最终equals版本!!!!!!
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof MyTime)) {
return false;
}
if (this == obj) {
return true;
}
MyTime tmp = (MyTime) obj;
return this.year == tmp.year && this.month == tmp.month && this.day == tmp.day;
}
}
//其他小例子:
//比较两个人是否是一个人,比较两个人的身份证是否相同即可
/*
class Person{
private String idCard;
private int height;
private int weight;
}
*/
String类重写了toString和equals
/*
java语言当中的字符串String有没有重写toString方法,有没有重写equals方法?
总结:
1.String类已经重写了equals方法,比较两个字符串不能使用"==",必须使用equals()。
equals()是调用的。
2.String类已经重写了toString()方法。
大大总结一下:
1.Java中什么类型的数据可以使用"=="判断?
java中基本数据类型比较是否相等,使用"=="
2.Java中什么类型的数据需要使用equals方法来判断?
java中所有的引用数据类型统一使用equals方法来判断是否相等。
*/
public class Main {
public static void main(String[] args) {
//大部分情况下,采用这样的方式创建字符串对象
String s1="hello";
String s2="abc";
//实际上String也是一个类。不属于基本数据类型
//既然String是一个类,那么一定存在构造方法。
String s3=new String("Test1");
String s4=new String("Test1");
//new两次,两个对象内存地址,s3保存的内存地址和s4保存的内存地址不同。
//"=="判断的是内存地址。不是内容
System.out.println(s3==s4);//false
//比较两个字符串能不能使用"=="?
//不能,必须调用equals方法。
//String类已经重写了equals方法了。
System.out.println(s3.equals(s4));//true
//String类有没有重写toString方法呢?
String x=new String("努力、上进");
//若String没有重写toString()方法,输出结果:java.lang.String@十六进制的地址
//经过测试:String类已经重写了toString(方法。)
System.out.println(x.toString());//努力、上进
System.out.println(x);//努力、上进
}
}
重写Object类的toString和equals
public class Main {
public static void main(String[] args) {
Student s1=new Student(111,"帅帅");
Student s2=new Student(111,"帅帅");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
}
}
class Student{
//学号
int no;
//所在学校
String school;
public Student(){}
public Student(int no,String school){
this.no=no;
this.school=school;
}
//重写toString方法
public String toString() {
return "学号:"+no+",所在学校:"+school;
}
//重写equals方法
//需求:当一个学生的学号相等,并且学校相同时时,表示同一个学生
//equals方法的编写模式都是固定的。架子差不多。
public boolean equals(Object obj) {
if(obj==null||!(obj instanceof Student)) return false;
if(obj==this) return true;
Student t=(Student) obj;
return this.school.equals(t.school)&&this.no==t.no;
}
}
总结toString和equals:
1)toString:
以后所有类的toString()方法是需要重写的。重写规则,越简单越明了越好。
System.out.println(引用); 这里会自动调用"引用"的toString()方法
String类时SUN写的,toString方法已经重写了。
2)equals:
以后所有类的equals方法也需要重写,因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。
重写规则:自己定,主要是什么和什么相等时表示两个对象相等。
基本数据类型比较实用: ==
对象和对象比较:调用equals方法
String类是SUN编写的,所以String类的equals方法重写了。
以后判断两个字符串是否相等,最好不要使用"==",要调用字符串对象的equals方法。
补充:equals()方法:
注意:重写equals方法的时候要彻底。
//equals方法重写要彻底
public class Main {
public static void main(String[] args) {
User u1=new User("张三",new Address("烟台","普照路","11111"));
User u2=new User("张三",new Address("烟台","普照路","11111"));
System.out.println(u1.equals(u2));//true
User u3=new User("张三",new Address("烟台","金辉路","11111"));
System.out.println(u1.equals(u3));//false
}
}
class User{
//用户名
String name;//保存的是地址(0x1234)
//用户的地址
Address addr;//保存的是地址(0x5210)
public User(){}
public User(String name,Address addr){
this.name=name;
this.addr=addr;
}
//重写equals方法
//重写规则:当一个用户的用户名和家庭住址都相同,表示同一个用户
//修改前:equals判断的是User对象和User对象是否相等
public boolean equals(Object o) {
//用户名和用户名相同,住址和住址相同的时候,认定为同一个用户
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return this.name.equals(user.name) && this.addr.equals(user.addr);
}
}
class Address{
String city;
String street;
String zipcode;
public Address(){}
public Address(String city,String street,String zipcode){
this.city=city;
this.street=street;
this.zipcode=zipcode;
}
//重写equals
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return this.city.equals(address.city) && this.street.equals(address.street) && this.zipcode.equals(address.zipcode);
}
}
hashCode方法:
在Object中的hashCode方法是怎样的?
public native int hashCode();
这个方法不是抽象方法,带有native关键字,底层调用C++程序。
hashCode()方法返回的是哈希码:
实际上就是一个Java对象的内存地址,经过哈希算法,得出的一个值。
所以hashCode()方法的执行结果可以等同看作一个java对象的内存地址。
public class Main {
public static void main(String[] args) {
Object o=new Object();
int hashCodeValue=o.hashCode();
//对象内存地址经过哈希算法转换的一个数字。可以等同看作内存地址。
System.out.println(hashCodeValue);//2003749087
Myclass mc=new Myclass();
System.out.println(mc.hashCode());//990368553
Myclass mc2=new Myclass();
System.out.println(mc2.hashCode());//1096979270
}
}
class Myclass{
}
09.匿名内部类
1.什么是内部类?
内部类:在类的内部又定义了一个新的类。被称为内部类。
2.内部类的分类:
静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量
3.使用内部类编写的代码,可读性很差。能不用尽量不用。
4.匿名内部类是局部内部类的一种。因为这个类没有名字而得名,叫做匿名内部类。
5.学习匿名内部类主要是为了能有阅读理解别人的代码
并不代表以后都要这么写。因为匿名内部类有两个缺点:
缺点1:太复杂,太乱,可读性太差。
缺点2:类没有名字,以后想重复使用,不能用。
public class Main {
//静态变量
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();
/*
Copute c=new ComputeImpl();
mm.MySum(c,100,200);
*/
//合并(这样写代码,表示这个类名是有的。类名是ComputeImpl)
//mm.mySum(new ComputeImpl(),100,200);
//使用匿名内部类,表示这个CompuImpl这个类没名字了。
//这里表面看上去好像是接口可以直接new了,实际上并不是接口可以new了。
//后面的{}代表了对接口的实现。
//不建议使用匿名内部类,为什么?
//因为一个类没有名字,没有办法重新使用。另外代码太乱,可读性太差。
mm.mySum(new Compute(){
public int sum(int a, int b) {
return a+b;
}
},100,200);
}
}
//负责计算的接口
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 val=c.sum(x,y);
System.out.println(x+"+"+y+"="+val);
}
}
10.数组
1.数组这种数据结构的优点和缺点是什么?
优点:查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
为什么检索效率高?
1)每一个元素的内存地址在空间存储上是连续的。
2)每一个元素类型相同,所以占用空间大小一样。
3)知道第一个元素的内存地址,知道每一个元素占用的空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高点。
缺点:
1)由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
2)数组不能存储大数据量,为什么?
因为很难在内存空间上找到一块特别大的连续的内存空间。
注意:对于数组中最后一给元素的增删,是没有效率影响的。
2.一维数组初始化模式:
1)静态初始化模式:
int[] a={1,2,3};
2)动态初始化模式:
int[] a=new int[3];
3.什么采用静态初始化方式,什么时候采用动态初始化方式?
1)当你创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式。
2)当你创建数组的时候,不确定将来数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。
4.数组拷贝:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
五个参数:第1个为源数组,第2个为源数组拷贝的起始位置,第3个为目标数组,第4个参数为拷贝到目标数组的下标位置,第5个为拷贝的长度
package com.jmpower.javese.array;
public class ArrestTest01 {
public static void main(String[] args) {
//拷贝源(从这个数组中拷贝)
int[] a={1,2,3};
//拷贝目标(拷贝到这个目标数组上)
int[] b=new int[5];
//调用JDK System类中的arraycopy方法,来实现数组的拷贝
//System.arraycopy(a,1,b,2,2);
//遍历目标数组
/*for(int i=0;i<b.length;i++)
{
System.out.println(b[i]);//0 0 2 3 0
}*/
System.arraycopy(a,0,b,0,a.length);
for (int i = 0; i < b.length; i++) {
System.out.println(b[i]);//1 2 3 0 0
}
Object[] obj=new Object[3];
Object[] newobj=new Object[5];
System.arraycopy(obj,0,newobj,0,obj.length);
for (int i = 0; i < newobj.length; i++) {
System.out.println(newobj[i]);
}
}
}
5.二维数组的初始化:
1)静态初始化:
int[][] array={
{1,2,3,4},
{99,54,21},
{56,42,12}
};
2)动态初始化:
int[][] array=new array[3][4];
数组模拟栈数据结构。
package com.jmoffer.javase.array;
public class Stack {
private Object[] a;
int size;//栈的大小
int t=-1;//栈帧
//setter和getter
public void setA(Object[] a) {
this.a = a;
}
public Object[] getA() {
return a;
}
//构造方法
public Stack(){}
public Stack(int size) {
this.size=size;
this.a=new Object[size];
}
//压栈
public void push(Object x){
if(t>=this.size-1)
{
System.out.println("栈满,压栈失败");
return ;
}
a[++t]=x;
System.out.println("压栈"+x+"成功,栈帧指向"+t);
}
//弹栈
public void pop(){
if(t<0)
{
System.out.println("栈空,弹栈失败");
return ;
}
System.out.print("弹栈"+a[t]+"成功,");
t--;
System.out.println("栈帧指向"+t);
}
}
class Test01{
public static void main(String[] args) {
Stack stk=new Stack(10);
for(int i=0;i<13;i++)
stk.push(new Object());
for(int i=0;i<3;i++)
stk.pop();
}
}
/*
压栈java.lang.Object@16b98e56成功,栈帧指向0
压栈java.lang.Object@7ef20235成功,栈帧指向1
压栈java.lang.Object@27d6c5e0成功,栈帧指向2
压栈java.lang.Object@4f3f5b24成功,栈帧指向3
压栈java.lang.Object@15aeb7ab成功,栈帧指向4
压栈java.lang.Object@7b23ec81成功,栈帧指向5
压栈java.lang.Object@6acbcfc0成功,栈帧指向6
压栈java.lang.Object@5f184fc6成功,栈帧指向7
压栈java.lang.Object@3feba861成功,栈帧指向8
压栈java.lang.Object@5b480cf9成功,栈帧指向9
栈满,压栈失败
栈满,压栈失败
栈满,压栈失败
弹栈java.lang.Object@5b480cf9成功,栈帧指向8
弹栈java.lang.Object@3feba861成功,栈帧指向7
弹栈java.lang.Object@5f184fc6成功,栈帧指向6
进程已结束,退出代码0
*/
11.酒店管理系统(第一个面向对象的小程序)
HotelMatSystem对象(前台):
package com.jmoffer.javase.array;
import java.util.Objects;
import java.util.Scanner;
/*
为某个酒店编写程序:酒店管理系统,模拟订房、退房、 打印所有房间状态等功能。
1、该系统的用户是:酒店前台。
2、酒店使用一个二维数组来模拟。“Room[][] rooms;"
3、酒店中的每一个房间应该是一个java对象:Room
4、每一个房间Room应该有:房间编号、房间类型、房间是否空闲.
5、系统应该对外提供的功能:
可以预定房间:用户输入房间编号,订房。
可以退房:用户输入房间编号,退房。
可以查看所有房间的状态:用户输入某个指分应该可以查看所有房间状态。
*/
public class HotelMgtSystem {
public static void main(String[] args) {
//创建酒店对象
Hotel hotel=new Hotel();
//打印房间状态
//hotel.print();
/*
首先输出一个欢迎页面
*/
System.out.println("欢迎使用酒店管理系统,请认真阅读以下使用说明");
System.out.println("功能编号对应的功能:[1]表示查看房间列表。[2]表示订房。[3]表示退房。[0]表示退出系统。");
Scanner in=new Scanner(System.in);
while(true){
System.out.println("请输入功能编号:");
int i = in.nextInt();
if(i==1) {
//查看房间列表
hotel.print();
} else if (i==2) {
//订房
System.out.println("请输入订房编号:");
int roomNo=in.nextInt();
hotel.order(roomNo);
} else if (i==3) {
//退房
System.out.println("请输入退房编号:");
int roomNo=in.nextInt();
hotel.exit(roomNo);
} else if (i==0) {
//退出系统
System.out.println("再见,欢迎下次光临!");
return ;
} else {
//出错了!
System.out.println("输入功能编号有误,请重新输入!");
}
}
}
}
Room对象:
package com.jmoffer.javase.array;
/*
房间对象
*/
public 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;
}
//sette和getter
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;
}
//boolean类型的变量,生成get方法的方法名是:isXxx()
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
//重写toString()
public String toString() {
return "["+no+","+type+","+(status?"空闲":"占用")+"]";
}
//重写equals()方法
//认为两个房间的房间号相同即为一个房间
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Room room = (Room) o;
return no == room.no;
}
//编写一个临时程序测试
/*public static void main(String[] args) {
Room room=new Room(101,"单人间",false);
System.out.println(room);
Room room1=new Room(101,"单人间",false);
System.out.println(room.equals(room1));
}*/
}
Hotel对象:
package com.jmoffer.javase.array;
/*
酒店对象
*/
public class Hotel {
/*
二维数组,模拟所有房间
*/
private Room[][] rooms;
//盖楼通过构造方法来盖楼
public Hotel(){
//一共有几层,每层的类型是什么,房间号是什么?
//假设一共三层:一层单人间,二层标准间,三层豪华间,每层10个房间
/*
房间编号:
一楼:101 102 103 .....
二楼:201 202 203 .....
三楼:301 302 303 .....
*/
//动态初始化
rooms=new Room[3][10];//共三层,每层10个房间
//创建30个Room对象,放到数组当中。
//怎么放呢?
for (int i=0;i<rooms.length;i++)//i是下标.i+1是楼层
{
for(int j=0;j<rooms[i].length;j++)
{
if(i==0)
{
rooms[i][j]=new Room((i+1)*100+j+1,"单人间",true);
}
else if(i==1)
{
rooms[i][j]=new Room((i+1)*100+j+1,"标准间",true);
}
else
{
rooms[i][j]=new Room((i+1)*100+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]);
}
System.out.println();
}
}
//订房方法
//调用此方法需要传递一个房间编号过来。编号是从前台小姐姐输入的。
public void order(int roomNo){
//订房最主要的是将房间对象的status修改为false
//Room对象的status修改为false
//假设房间编号为207( 即room[1][6] )
//修改为占用
if(!rooms[roomNo/100-1][roomNo%100-1].isStatus())
{
System.out.println(roomNo+"已被占用,无法预定!");
}
else {
rooms[roomNo/100-1][roomNo%100-1].setStatus(false);
System.out.println(roomNo+"已订房!");
}
}
//退房方法
public void exit(int roomNo){
//修改为空闲
rooms[roomNo/100-1][roomNo%100-1].setStatus(true);
System.out.println(roomNo+"已退房!");
}
}
12.简单算法
冒泡排序法
package com.jmoffer.javase.array;
import java.util.Arrays;
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
/*int[] arr={1,554,1,5,41,3};*/
int[] arr=new int[10];
Scanner in=new Scanner(System.in);
for(int i=0;i<10;i++)
{
arr[i]=in.nextInt();
}
for(int i=0;i<arr.length-1;i++)
{
for(int j=0;j<arr.length-1-i;j++)
{
if(arr[j]>arr[j+1]){
int t=arr[j];
arr[j]=arr[j+1];
arr[j+1]=t;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
选择排序法
package com.jmoffer.javase.array;
import java.util.Scanner;
public class Test03 {
public static void main(String[] args) {
int[] arr=new int[10];
Scanner in =new Scanner(System.in);
for(int i=0;i<10;i++)
{
arr[i]=in.nextInt();
}
for(int i=0;i<arr.length-1;i++)
{
int mi=i;
for(int j=i+1;j<arr.length;j++)
{
//找到后面的最小值
if(arr[j]<arr[mi]){
mi=j;//最小值下标
}
}
//获取后面的最小值与下标i数据进行比较
if(arr[i]>arr[mi])
{
int t=arr[i];
arr[i]=arr[mi];
arr[mi]=t;
}
}
for (int i=0;i<arr.length;i++)
{
System.out.println(arr[i]);
}
}
}
二分查找
package com.jmoffer.javase.array;
import java.util.Arrays;
import java.util.Scanner;
public class Test04 {
public static void main(String[] args) {
int[] arr=new int[10];
Scanner in=new Scanner(System.in);
for(int i=0;i<arr.length;i++)
{
arr[i]=in.nextInt();
}
Arrays.sort(arr);
for (int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
int pos=ArraySearch(arr,5);
System.out.println(pos==-1?"该元素不存在":"该元素下标为:"+pos);
}
public static int ArraySearch(int[] arr,int t){
Arrays.sort(arr);
int l=0,r=arr.length-1;
while(l<r)
{
int mid=l+r>>1;
if(arr[mid]>=t) r=mid;
else l=mid+1;
}
if(arr[r]==t) return r;
return -1;
}
}
使用Arrays工具类进行二分查找(具有不稳定性)
package com.jmoffer.javase.array;
import java.util.Arrays;
import java.util.Scanner;
public class Test04 {
public static void main(String[] args) {
int[] arr=new int[10];
Scanner in=new Scanner(System.in);
for(int i=0;i<arr.length;i++)
{
arr[i]=in.nextInt();
}
//排序
Arrays.sort(arr);
for (int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
//二分查找
int pos=Arrays.binarySearch(arr,5);
System.out.println(pos<0?"该元素不存在":"该元素下标为:"+pos);
}
}
13.String类
String字符串的存储原理:
字符串存储在方法区的字符串常量池当中。
样例1:
public class StringTest02 {
public static void main(String[] args) {
//i变量中保存的是100这个值
int i=100;
//s变量中保存的是字符串对象的对内地址
//s引用中保存的不是"abc",是0x1111
//而0x1111是"abc"字符串对象在"字符串常量池"当中的内存地址。
String s="abc";
}
}
样例2:
public class StringTest01 {
public static void main(String[] args) {
String s1="hello";
//"hello"是存储在方法区的字符串常量池当中
//所以这个"hello"不会新建。(因为这个对象已经存在了!)
String s2="hello";
//== 双等号比较的是变量中保存的内存地址!
System.out.println(s1==s2);//true
String x=new String("jm");
String y=new String("jm");
//== 双等号比较的是变量中保存的内存地址!
System.out.println(x==y);//false
//字符串对象之间的比较不能使用"=="
//"=="不保险。应该调用String类的equals方法
//String类已经重写了equals方法,以下的equals方法调用的是String重写之后的equals方法
System.out.println(x.equals(y));//true
String k=new String("yxc");
/*String k=null;*/
System.out.println(k.equals("yxc"));//NullPointerException
System.out.println("yxc".equals(k));//建议使用这种方式,因为这样写可以避免空指针异常的风险
}
}
样例3:
public class StringTest03 {
public static void main(String[] args) {
/*
一共3个对象:
方法区字符串常量池中有1个:"hello"
堆内存当中有两个String对象。
一共3个。
*/
String s1=new String("hello");
String s2=new String("hello");
}
}
String类常用的构造方法:
/*
关于String类的构造方法:
1)String s=new String("");
2)String s="";//最常用
3)String s=new String(char数组);
4)String s=new String(char数组,起始下标,长度);
5)String s=new String(byte数组);
6)String s=new String(byte数组,起始下标,长度);
*/
public class StringTest04 {
public static void main(String[] args) {
//创建字符串对象最常用的一种方式
String s1="hello world!";
//s1这个变量中保存的是一个内存地址。
//按说以下应该输出一个地址
//但是输出是一个字符串,说明String类已经重写了toString()方法
System.out.println(s1.toString());//hello world!
System.out.println(s1);//hello world!
byte[] bytes={97,98,99};//97是a,98是b,99是c
String s2=new String(bytes);//abc
//通过输出结果得出一个结论:String类已经重写了toString()方法
//输出字符串对象的话,输出的不是对象的内存地址,而是字符串本身
System.out.println(s2.toString());//abc
System.out.println(s2);//abc
//String(字节数组,数组元素的下标的起始位置,长度)
String s3=new String(bytes,1,2);
System.out.println(s3);//bc
//将char数组全部转换成字符串
char[] chars={'我','是','中','国','人'};
String s4=new String(chars);
System.out.println(s4);//我是中国人
//将char数组的一部分转换成字符串
String s5=new String(chars,2,3);
System.out.println(s5);//中国人
String s6=new String("baby");
System.out.println(s6);//baby
}
}
String类常用的方法:
import java.util.Locale;
public class StringTest06 {
public static void main(String[] args) {
//String类中常用方法:
//1.(掌握)char charAt(int index);
//返回 char指定索引处的值。
char c="中国人".charAt(1);
System.out.println(c);//国
//2.(了解)int compareTo(String anotherString)
//按照字典序比较两个字符串的大小(可以看出来谁大谁小)
//boolean equalsIgnoreCase(String anotherString)
//将此 String与其他 String比较,忽略大小写。
int result="abc".compareTo("abc");
System.out.println(result);//相同返回:0
int result2="abc".compareTo("abcd");
System.out.println(result2);//前小后大返回:负数
int result3="abd".compareTo("abc");
System.out.println(result3);//前大后小返回:正数
System.out.println("abc".compareToIgnoreCase("ABc"));//相同:0
System.out.println("aac".compareToIgnoreCase("Bnd"));//负数
System.out.println("bac".compareToIgnoreCase("Abc"));//正数
//3.(掌握)boolean contains(CharSequence s)
//当且仅当此字符串包含指定的char值序列时才返回true。
System.out.println("abc".contains("a"));//true
System.out.println("http".contains("https"));//false
//4.(掌握)boolean endsWith(String suffix)
//测试此字符串是否以指定的后缀结尾。
System.out.println("test.txt".endsWith(".txt"));//true
System.out.println("text.txt".endsWith(".java"));//false
//5.(掌握)boolean equals(Object anObject)
//将此字符串与指定对象进行比较。(看不出来谁大谁小,只能看出来是否相等)
//equals只能看出来是否相等
//compareTo方法可以看出是否相等,同时看出谁大谁小
System.out.println("abc".equals("abc"));//true
System.out.println("abc".equals("ab"));//false
//6.(掌握)boolean equalsIgnoreCase(String anotherString)
//将此 String与其他 String比较,忽略大小写。
System.out.println("Abc".equalsIgnoreCase("abc"));//true
System.out.println("aBc".equalsIgnoreCase("aBb"));//false
//7.(掌握)byte[] getBytes()
//使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。
byte[] bytes="abcdef".getBytes();
for(int i=0;i<bytes.length;i++){
System.out.print(bytes[i]+" ");//97 98 99 100 101 102
}
System.out.println();
//8.(掌握)int indexOf(int ch)
//判断某个子字符串在当前字符串中第一次出现的索引(下标)。
System.out.println("com.jmpower.javase.String".indexOf("jm"));//4
//9.(掌握)boolean isEmpty()
//返回 true如果,且仅当 length()为 0 。
String s="a";
System.out.println(s.isEmpty());//false
//10.(掌握)int length()
//返回此字符串的长度。
//面试题:判断数组长度和判断字符串长度不一样
//判断数组长度是length属性,判断字符串长度是length()方法
System.out.println("abc".length());//3
System.out.println("".length());//0
byte[] a={1,2,3};
System.out.println(a.length);//3
//11.(掌握)int lastIndexOf(String str)
//返回指定子字符串最后一次出现的字符串中的索引。
System.out.println("com.jmpower.javase.Stringjm".lastIndexOf("jm"));//25(从后查找)
System.out.println("com.jmpower.javase.Stringjm".indexOf("jm"));//4(从前查找)
//12.(掌握)String replace(CharSequence target, CharSequence replacement)
//将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
//String类的父接口就是CharSequence
String newString="http://www.baidu.com".replace("http","https");//https://www.baidu.com
System.out.println(newString);
//13.(掌握)String[] split(String regex)
//拆分字符串。
String[] yxc="1980-10-11".split("-");
for(int i=0;i< yxc.length;i++)
{
System.out.print(yxc[i]+" ");//1980 10 11
}
System.out.println();
//14.(掌握)boolean startsWith(String prefix)
//测试此字符串是否以指定的前缀开头。
System.out.println("abc.edg.www".startsWith("abc"));//true
System.out.println("abc.yxc.www".startsWith("yxc"));//false
//15.(掌握)String substring(int beginIndex) 参数是起始下标
//返回一个字符串,该字符串是此字符串的子字符串。
System.out.println("http://www.baidu.com".substring(7));//www.baidu.com
//16.(掌握)String substring(int beginIndex, int endIndex)
//返回一个字符串,该字符串是此字符串的子字符串。
//beginIndex是起始下标(包括)
//endIndex是结束下标(不包括)
System.out.println("abcdefghijkl".substring(2,5));//cde
//17.(掌握)char[] toCharArray()
//将此字符串转换为新的字符数组。
char[] chars="我是中国人".toCharArray();
for(int i=0;i< chars.length;i++)
{
System.out.print(chars[i]+" ");//我 是 中 国 人
}
System.out.println();
//18.(掌握)String toLowerCase()
//将所有在此字符 String使用默认语言环境的规则,以小写。
System.out.println("ASDbsfH".toLowerCase());//asdbsfh
//19.(掌握)String toUpperCase()
//将所有在此字符 String使用默认语言环境的规则大写。
System.out.println("SsadfFS".toUpperCase());//SSADFFS
//20.(掌握)String trim()
//返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
System.out.println(" abcdefg ".trim());//abcdefg
//21.(掌握)String中只有一个方法是静态的,不需要new对象
//这个方法叫valueOf
//作用:将"非字符串"转换成"字符串"
String s1=String.valueOf(100);
String s2=String.valueOf(3.14);
String s3=String.valueOf(true);
String s4=String.valueOf('c');
System.out.println(s1+" "+s2+" "+s3+" "+s4);//100 3.14 true c
//这个静态的valueOf()方法,参数是一个对象的时候,会自动调用对象的toString()方法!
String ss1=String.valueOf(new User());
//System.out.println(ss1);//没有重写toString()方法之前是对象内存地址。User@34a245ab
System.out.println(ss1);//重写toString()方法之后:我是一个VIP用户!!!
}
}
class User{
public String toString() {
return "我是一个VIP用户!!!";
}
}
14.StringBuffer类和StringBuilder类
使用String直接拼接字符串的坏处?
/*
思考:在实际开发中,如果需要进行字符串的频繁拼接,会有什么问题呢?
因为java中的字符串是不可变的,每一次拼接都会产生新字符串。
这样会占用大量的方法区内存。造成内存空间的浪费。
String s = "abc";
s += "hello";
就以上两行代码,就导致在方法区字符串常量池中创建了3个对象:
"abc"
"hello"
"abchello"
*/
public class StringBufferTest01 {
public static void main(String[] args) {
String s="";
for(int i=0;i<100;i++)
{
s+=i;
System.out.println(s);
}
}
}
StringBuffer的应用:
/*
如果以后需要大量字符串拼接操作,建议使用JDK自带的:
java.lang.StringBuffer
jave.lang.StringBuilder
如何优化StringBuffer的性能?
在创建StringBuffer的时候尽可能给定一个初始化容量。
最好减少底层数组的扩容次数。预估计一下,给一个大一些初始化容量。
关键点:给一个合适的初始化容量。可以提高效率。
*/
public class StringBufferTest02 {
public static void main(String[] args) {
//创建一个初始化容量为16个Byte[] 数组。(字符串缓冲区对象)
StringBuffer stringBuffer=new StringBuffer();
//拼接字符串,以后拼接字符串统一调用append()方法。
stringBuffer.append("a");
stringBuffer.append("b");
stringBuffer.append(3.14);
stringBuffer.append(true);
stringBuffer.append(100L);
//append方法底层再进行追加的时候,如果byte数组满了,会自动扩容
System.out.println(stringBuffer);//ab3.14true100
//指定初始化容量的StringBuffer对象(字符串缓冲区对象)
StringBuffer sb=new StringBuffer(100);
sb.append("hello");
sb.append("world");
sb.append('w');
sb.append(555);
System.out.println(sb);//helloworldw555
}
}
StringBuffer和StringBuilder的区别?
/*
java.lang.StringBuilder
StringBuffer和StringBuilder的区别?
StringBuffer中的方法都有:synchronized关键词修饰,表示StringBuffer在多线程环境下运行是安全的。
StringBuilder中的方法都没有:synchronized关键词修饰,表示StringBuilder在多线程下运行是不安全的。
StringBuffer是线程安全的。
StringBuilder是非线程安全的。
*/
public class StringBuilderTest01 {
public static void main(String[] args) {
//使用StringBuilder也是可以完成字符串的拼接
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("hello");
stringBuilder.append("world");
stringBuilder.append("555");
System.out.println(stringBuilder);//helloworld555
}
}
15.包装类型
/*
1.Java中为8种基本数据类型又对应准备了8种包装类型。8种包装类型属于引用数据类型,父类是Object。
2.思考:为什么要再提供8种包装类呢?
因为8种基本数据类型不够用。
所以SUN又提供对应的8种包装类型。
*/
public class IntegerTest01 {
//入口
public static void main(String[] args) {
//有没有这种需求:调用doSome()方法的时候需要传一个数字进去。
//但是数字属于基本数据类型,而doSome()方法参数的类型是Object。
//可见doSome()方法无法接受基本数据类型的数字。那怎么办?可以传一个数字对应的包装类进去。
//把100这个数字经过构造方法包装成对象
MyInt myInt=new MyInt(100);
//doSome()方法虽然不能直接传100,但是可以传一个100对应的包装类型。
doSome(myInt);
}
public static void doSome(Object obj){
System.out.println(obj);//会自动调用toString(),记得重写
}
}
class MyInt{
int i;
public MyInt(){}
public MyInt(int i){
this.i=i;
}
//重写toString()
public String toString() {
return String.valueOf(i);
}
}
8种基本数据类型对应的包装类型名是什么?
(都在java.lang包下)
byte ~ Byte(父类java.lang.Number)
short ~ Short(父类java.lang.Number)
int ~ Integer(父类java.lang.Number)
long ~ Long(父类java.lang.Number)
float ~ Float(父类java.lang.Number)
double ~ Double(父类java.lang.Number)
boolean ~ Boolean(父类java.lang.Object)
char ~ Character(父类java.lang.Object)
装箱与拆箱(JDK9之前支持)
Test01
装箱和拆箱的概念:
public class Test01 {
public static void main(String[] args) {
//基本数据类型-(转换)->引用数据类型 《装箱》
Integer x=new Integer(123);
//引用数据类型-(转换)->基本数据类型 《拆箱》
float f=x.floatValue();
System.out.println(f);//123.0
int i=x.intValue();
System.out.println(i);//123
}
}
Test02
包装类的构造方法:
以Integer为例: 1)Integer(int) 2)Integer(String)
剩下的以此类推!
public class Test02 {
public static void main(String[] args) {
//JDK9之后就不支持这种写法了,出现横线表示已经过时
//int->Integer
Integer x=new Integer(100);
System.out.println(x);
//String->Integer
Integer y=new Integer("100");
System.out.println(y);
//double->Double
Double m=new Double(3.14);
System.out.println(m);
//String->Double
Double n=new Double("3.14");
System.out.println(n);
}
}
常量的最大值与最小值
常量的最大值: 包装类型名.MAX_VALUE
常量的最小值: 包装类型名.MIN_VALUE
public class Test03 {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);//2147483647
System.out.println(Integer.MIN_VALUE);//-2147483648
System.out.println(Byte.MAX_VALUE);//127
System.out.println(Byte.MIN_VALUE);//-128
System.out.println(Character.MAX_VALUE);
}
}
自动装箱与自动拆箱(JDK1.5之后支持)
例子1(自动拆箱与自动装箱操作)
/*
自动装箱:基本数据类型自动转换为包装类。
自动拆箱:包装类自动转换为基本数据类型。
好处:方便编程。
*/
public class Test04 {
public static void main(String[] args) {
//100是基本数据类型
//x是包装类型
//基本数据类型--(自动转换)-->包装类型 《自动装箱》
Integer x=100;
System.out.println(x);
//x是包装类型
//y是基本数据类型
//包装类型--(自动转换)-->基本数据类型 《自动拆箱》
int y=x;
System.out.println(y);
//z是一个引用,z还是保存了一个对象的内存地址
Integer z=1000;//等同于Integer z=new Integer(1000);
//这里没有报错
//+两边都是基本数据类型,z是包装类,这里进行了自动类型转换,z自动转换为了基本数据类型
System.out.println(z+1);
Integer a=1000;
Integer b=1000;
//== 比较的是两个对象的内存地址。a,b引用中保存的对象内存地址不同。
//== 不会触发拆箱
System.out.println(a==b);//false
}
}
样例2(面试题)
/*
分析以下程序是为什么?
*/
public class Test05 {
public static void main(String[] args) {
Integer a=127;
Integer b=127;
System.out.println(a==b);//true
/*
java为了提高程序的效率,将[-128,127]之间所有的包装对象提前创建好,
放到了一个方法区的"整数型常量池"当中了,目的是只要用这个区间的数据不需要
再new了,直接从整数型常量池当中取出来。
原理:x变量中保存的对象的内存地址和y变量中保存的对象的内存地址是一样的。
*/
Integer x=128;
Integer y=128;
System.out.println(x==y);//false
}
}
缓存机制:cache
大型项目中的重要优化手段就是:cache
优点:效率高
缺点:耗费内存
Integer类有哪些常用的方法?
(其他包装类,以此类推)
//Integer类当中有哪些常用的方法呢?
public class Test06 {
public static void main(String[] args) {
//1. intValue()
//将 Integer的值作为 int 。
//手动装箱
Integer x=new Integer(1000);
//手动拆箱
int y=x.intValue();
System.out.println(y);
//2.重点方法
//static int parseInt(String s)
//静态方法,传参String,返回int
//网页文本框上输入的100实际上是"100"字符串。后台数据要求存储100数字,此时java程序需要将"100"字符串转换成100数字。
int retvalue=Integer.parseInt("123");//String->int
//int revalue=Integer.parseInt("中文");//异常: NumberFormatException
System.out.println(retvalue+100);//223
//照葫芦画瓢(用类名调用方法,说明是静态方法)
double retvalue2=Double.parseDouble("3.14");
System.out.println(retvalue2+1);//4.140000000000001(精度问题)
float retvalue3=Float.parseFloat("1.0");
System.out.println(retvalue3+1);//2.0
//3.valueOf(int i)
//将字符串转换成int型
Integer c=Integer.valueOf("456");
System.out.println(c);
}
}
String、int、integer之间相互转换
/*
String、int、integer之间相互转换
*/
public class Test07 {
public static void main(String[] args) {
//String->int
String s1="100";
int i1=Integer.parseInt(s1);
System.out.println(i1+1);//101
//int->String
String s2=i1+"";
System.out.println(s2+1);//"1001"
//int->Integer(自动装箱)
Integer x=1000;
System.out.println(x);
//Integer->int(自动拆箱)
int y=x;
System.out.println(y);
//String->Integer
Integer k=Integer.valueOf("123");
System.out.println(k);//123
//Integer->String
String e=String.valueOf(k);
System.out.println(e);
}
}
16.java对日期的处理
样例1
import java.text.SimpleDateFormat;
import java.util.Date;
/*
java中对日期的处理
这个案例主要掌握:
知识点1: 怎么获取系统当前时间
知识点2: String--->Date
知识点3: Date--->String
*/
public class DateTest01 {
public static void main(String[] args) throws Exception{
//知识点1:--------------------------
//获取当前时间(精确到毫秒的系统当前时间)
//直接调用无参数构造方法就行
Date nowDate=new Date();
System.out.println(nowDate);Mon Jun 27 09:16:49 CST 2022
//java.util.Date类的toString()方法已经被重写了
//输出的应该不是一个对象的内存地址,应该是一个日期字符串
//知识点2:---------------------------
//日期可以格式化吗?
//将日期类型Date,按照指定的格式进行转换:Date--转换成具有一定格式的日期字符串-->String
//SimpleDateFormat是java.text包下的。专门负责日期的格式化
/*
yyyy 年(年是4位)
MM 月(月是2位)
dd 日
HH 时
mm 分
ss 秒
SSS毫秒(毫秒三位,最高999,当1000毫秒代表1秒)
注意:在日期格式中,除了y M d H m s S这些字符不能随便写之外,剩下的符号格式自己随意组织。
*/
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
//SimpleDateFormat sdf=new SimpleDateFormat("dd/MM/yyyy");
//SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");
String nowTimeStr=sdf.format(nowDate);
System.out.println(nowTimeStr);//2022-06-27 09:34:57 938
//知识点3:---------------------------
//假设现在有一个日期字符串String,怎么转换成Date类型?
String time="2008-08-08 08:08:08 888";
//SimpleDateFormat sdf2=new SimpleDateFormat("");(格式不能随便写,要和日期字符串格式相同)
//注意: 字符串的日期格式和SimpleDate对象指定的日期格式要一致。不然会出现异常:java.text.ParseException
SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
Date dateTime=sdf2.parse(time);
System.out.println(dateTime);//Fri Aug 08 08:08:08 CST 2008
}
}
样例2
/*
获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数
1秒 = 1000毫秒
简单总结一下System类的相关属性和方法:
System.out 【out是System类的静态方法】
System.out.println() 【println()方法不是System类的,是printStream类】
System.gc() 建议启动垃圾回收器
System.currentTimeMillis() 获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数
System.exit(0) 退出JVM
*/
public class DateTest02 {
public static void main(String[] args) {
//获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数
long nowTimeMilli=System.currentTimeMillis();
System.out.println(nowTimeMilli);//1656295098216
//统计一个方法的耗时
//在调用目标方法之前记录一个毫秒数
long begin=System.currentTimeMillis();
print();
//在执行完目标方法之后记录一个毫秒数
long end=System.currentTimeMillis();
System.out.println("耗费时长"+(end-begin)+"毫秒");
}
//需求: 统计一个方法执行所耗费的时长
public static void print(){
for(int i=0;i<1000;i++)
{
System.out.println("i="+i);
}
}
}
样例3
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateTest03 {
public static void main(String[] args) {
//这个时间是什么时间?
//1970-01-01 00:00:00 001
Date time=new Date(1);//注意,参数是一个毫秒
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime=sdf.format(time);
//北京是东8区。差8个小时
System.out.println(strTime);//1970-01-01 08:00:00 001
//获取昨天的此时的时间
Date time2=new Date(System.currentTimeMillis()-24*60*60*1000);
String strTime2=sdf.format(time2);
System.out.println(strTime2);//2022-06-26 11:02:07 174
}
}
17.java对数字的处理
样例1
import java.text.DecimalFormat;
/*
关于数字的格式化
*/
public class DecimalFormatTest01 {
public static void main(String[] args) {
//java。test。DecimalFormat 专门负责数字格式化的。
//DecimalFormat df=new DecimalFormat("数字格式");
/*
数字格式有哪些?
# 代表任意数字
, 代表千分位
. 代表小数点
0 代表不够时补0
###,##.##
表示: 加入千分位,保留2位小数
*/
DecimalFormat df=new DecimalFormat("###,###.##");
String s=df.format(1234.561232);
System.out.println(s);//"1,234.56"
DecimalFormat df2=new DecimalFormat("###,##.0000");//保留4个小数位,不够补0
String s2=df2.format(1234.56);
System.out.println(s2);//"12,34.5600"
}
}
样例2
import java.math.BigDecimal;
/*
1.BigDecimal 属于大数据,精度极高。不属于基本数据类型,属于java对象(引用数据类型)
这是SUN提供的一个类。专门用在财务软件当中。
2.注意:财务软件在double是不够用的。要用java.math.BigDecimal
*/
public class BigDecimalTest01 {
public static void main(String[] args) {
//这个100不是普通的100,是精度极高的100
BigDecimal v1=new BigDecimal(100);
//精度极高的200
BigDecimal v2=new BigDecimal(200);
//求和
//v1+v2; 不可以这样写,v1和v2都是引用,不能之间使用+求和。
BigDecimal v3=v1.add(v2);//调用方法求和
System.out.println(v3);//300
BigDecimal v4=v2.divide(v1);
System.out.println(v4);//2
}
}
18.java对随机数的处理
样例1
import java.util.Random;
public class RandomTest01 {
public static void main(String[] args) {
//创建随机数对象
Random random=new Random();
//随机产生一个int类型取值范围内的数字
int num1=random.nextInt();
System.out.println(num1);
//产生[0-100]之间·的随机数。不能产生101.
//nextInt翻译为:下一个int类型的数据是101,表示只能取到100.
int num2=random.nextInt(101);//不包括101
System.out.println(num2);
}
}
样例2
/*
随机产生5个0-100不重复的数,存到数组中
*/
import java.util.Arrays;
import java.util.Random;
public class RandomTest02 {
public static void main(String[] args) {
int[] a=new int[5];
int[] b=new int[110];//标记已经存在过的数据
Random random=new Random();//创建random对象
int idx=0;
for(int i=0;i<a.length;i++) a[i]=-1;
while(idx<a.length)
{
int num=random.nextInt(101);//随机取出[0,100]中的数
if(b[num]==0)
{
b[num]=1;//记录a[]中已经存在num
a[idx++]=num;
}
}
for(int i=0;i<5;i++)
{
System.out.print(a[i]+" ");
}
}
}
19.枚举类型
样例1
package com.jmpower.javase.enum2;//标识符,关键字不能做标识符。enum是关键字
/*
分析以下程序,在设计方面有什么缺陷?
*/
public class EnumTest01 {
public static void main(String[] args) {
boolean retValue=divide(10,2);
System.out.println(retValue);//true
boolean retValue2=divide(10,0);
System.out.println(retValue2);//false
}
/**
* 以下程序,计算两个int类型数据的商,计算成功返回1,计算失败返回0
* @param a int类型的数据
* @param b int类型的数据
* @return 返回1表示成功,返回0表示失败!
*/
public static boolean divide(int a,int b){
try{
int c=a/b;
//程序执行到此处,表示以上代码没有异常。表示执行成功!
return true;
} catch(Exception e){
//程序执行到此处,表示以上程序出现了异常!
//表示执行失败!
return false;
}
}
}
样例2
package com.jmpower.javase.enum2;
//采用枚举的方式改造程序
/*
总结:
1.枚举是一种引用数据类型
2.枚举类型怎么定义,语法是?
enum 枚举类型名{
枚举值1,枚举值2
}
3.结果只有两种情况的,建议使用布尔类型。结果超过两种并且可以一枚一枚列举出来的,建议使用枚举。
例如:颜色、四季、星期等都可以使用枚举类型。
*/
public class EnumTest02 {
public static void main(String[] args) {
Result r= divide(10,2);
System.out.println(r==Result.SUCCESS?"计算成功":"计算失败");
}
/**
* 计算两个int类型数据的商
* @param a int数据
* @param b int数据
* @return Result.SUCCESS表示成功,Result.FAIL表示失败!
*/
public static Result divide(int a,int b){
try {
int c=a/b;
return Result.SUCCESS;
} catch (Exception e){
return Result.FAIL;
}
}
}
//枚举: 一枚一枚可以列举出来的,才建议使用枚举类型。
//枚举编译之后也是生成class文件
//枚举也是一种引用数据类型
//枚举中的每一个值可以看做是常量
enum Result{
//SUCCESS 是枚举Result类型中的一个值
//FAIL 是枚举Result类型中的一个值
//枚举中的每一个值,可以看作是”常量“
SUCCESS,FAIL
}
样例3
package com.jmpower.javase.enum2;
/**
* 四季枚举类型
*/
public enum Season {
/*
春夏秋冬
*/
SPRINT,SUNMER,AUTUMN,WINTER
}
样例4-------------------------------------------
package com.jmpower.javase.enum2;
public enum Color {
/*
颜色值
*/
REN,BLUE,YELON,BLACK
}