Java基础

程序运行

终端javac编译成.class,使用java运行,javadoc生成注释文档

基本数据类型

floatdouble为近似值
byteshortint三者在计算时会转换为int类型,可以使用强制类型转换

short s1 = 10;
int s2 = s1+1;//必须加int,因为类型变了
s1 = (short)(s1+1);//强制类型转换
s1 +=1;//没问题

键盘输入

要先引入Scanner类才能使用键盘输入

import java.util.Scanner

运算符

&,|即可以表示逻辑运算符,也可以表示按位运算符;表示逻辑运算符时针对boolean变量,先计算两边的值再做比较
按位运算符计算乘除结果比普通乘除快(是最快的方式):>>除以2的n次方;<<乘以2的n次方

两个变量交换的三种方法

int a=10,b=12;
//第一种方法
int c=a;
a=b;
b=c;

//第二种方法
a = a+b-(b=a);

//第三种方法
a = a+b;
b = a-b;
a = a-b;

//第四种方法
a = a*b;
b = a/b;
a = a/b;

//第五种方法(最快)不能是相同值
a = a^b;
b = a^b;
a = a^b;

方法重载

在一个类中可以有多个同名的方法,参数和定义不同,但返回值必须一样,将根据参数类型自动匹配相应的方法

数组

java定义数组的方式

//第一种
int[] nums1 = new int[5];

//第二种
int[] nums2;
nums = new int[5];

//第三种
int[] nums3 = new int[]{1,2,3,4,5};

//第四种
int[] nums4 = {1,2,3,4,5};

x.length表示数组的长度
foreach遍历数组:每次读取数组的一个值并赋值给num

for(int num:nums1){}

可变参数:只能定义在参数列表的最后一个,表示参数个数可变,作为数组使用

public stasic void fun(int... 变量名){}

数组存储:数组是引用类型,栈内存存储地址,指向堆内存的值

Array类常用方法

  • **Arrays.binarySearch(int[] array,int value);**:二分查找
  • Arrays.toString(int[] array);:将数组转换成字符串打印
  • Arrays.sort(int[] array);:数组排序
  • Arrays.copyOf(int[] array,int length); Arrays.copyOf(int[] array,int from,int to); **System.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)**(原数组,原数组起始位置,目标数组,目标数组起始位置,复制长度) 数组复制
  • Arrays.equels();:判断两个数组是否相等,不能用==,==比较的是地址
  • Arrays.fill();:填充数组

内存机制

栈内存:大小固定,用于存储局部,临时变量(基本数据类型)和引用变量(存储地址)
堆内存:大小不固定,存储对象

面向对象

存储机制

变量存储在栈内存中,当为空类时堆内存不存内容,栈内存的变量无地址;
当给变量实例化后,栈内存中存储地址指向堆内存的值;
当堆中内存没有任何一个栈内存指向它时,会被JVM的GC程序认为是垃圾而回收

public class Horse{
    String name;//引用变量,在堆内存中存储的是指向具体值的地址
    int age;
    public void(){
        System.out.priintln("我是"+name+"今年"+age+"岁了");
    }

Horse horse1 = null;//空类,栈内存无地址指向堆内存
Horse horse2 = new Horse();//实例化,栈内存存储地址指向堆内存的name和age
horse2.name = "小黑";
horse2.age = "500";
//对象赋值
Horse horse3 = null;
horse3 = horse2;//相同类的对象之间才可以赋值,此时栈内存中变量horse2,horse3指向同一块堆内存地址
horse3.name = "小白";//此时horse2.name跟着改变,因为两者存储地址一致

Horse horse4 = new Horse();
horse4.name = "小红";
horse4 = horse2;//“小红”被当为垃圾

horse2 = null;//释放对象
}

封装

类中的方法存储在方法区,调用时进入栈内存,调用完之后回到方法区
成员变量有默认值,成员变量使用前必须先赋值
在方法中使用变量,使用就 近原则

构造方法

构造方法在new对象的时候自动最先执行
方法名称与类名相同,没有返回值声明

class Dog{
    public Dog(){
        //内容为空时为默认的构造方法
        System.out.println("构造方法");
    }
    //构造方法的重载
    public Dog(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("方法重载");
    }
    //构造方法的相互调用
    public Dog(String name,int age,int sex){
        this(name,age);//调用其他构造方法,只能放在第一句
        this.sex = 1;
    }

    String name;
    int age;
    int sex;
}

Dog dog1 = new Dog();
Dog dog2 = new Dog("旺旺",5);

private Dog(){}:私有构造方法,私有化后该类不能new新对象

this关键字

哪个对象使用this,this就表示该类
类名.this.xxx:调用某个指定类中的方法,变量

值传递和引用传递

字符串String相当于一个对象,当调用改变时,字符串变量指向新的堆内存,而不是改变地址上的值,但类中的String型变量指向堆内存的地址上存储的是值的地址,当调用方法改变时,会改变堆内存中值的地址的值,所以变量的值会改变

public class RefDemo{
    public static void main(String[] args){
        int a = 1;
        String name  = "小飞";
        method1(a,name);
        Duck d = new Duck();
        method2(d);
        System.out.println("a = "+a);//a=1
        System.out.println("name = "+name);//name=小飞
        System.out.println("d.age = "+d.age);//d.age=5
        System.out.println("d.name = "+d.name);//d.name=大黄鸭
        
    }
    public static void method1(int sa,String sname){
        sa = 2;
        sname = "小备";
    }
    public static void method2(Duck duck){
        duck.age = 5;
        duck.name = "大黄鸭";
    }
}
class Duck{
    int age = 2;
    String name = "小黄鸭";
}

对象的一对一关系

class Hero{
    private String name;
    private int age;
    private Weapen weapen;//一对一(双向)
    public void setWeapen(Weapen weapen){
        this.weapen = weapen;
    }
    public Weapen getWeapen(){
        return weapen;
    }
   public Hero(){} 
   public Hero(String name,int age){
       this.name = name;
       this.age = age;
   }
   public void setName(String name){
       this.name = name;
   }
   public String getName(){
       return name;
   }
   public void setAge(int age){
       this.age = age;
   }
   public int getAge(){
       return age;
   }
}

public Weapen{
    private String name;
    private int grade;
    private Hero hero;//一对一关系,双向
    public void setHero(Hero hero){
        this.hero = hero;
    }
    public Hero getHero(){
        return hero;
    }
    public Weapen(){}
    public Weapen(String name,int grade){
        this.name = name;
        this.grade = grade;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setGrade(int grade){
        this.grade = grade;
    }
    public int getGrade(){
        return grade;
    }
}

static关键字

  1. 定义的变量、方法不属于对象,而依赖于类
  2. 定义的变量时全局变量,生命周期从类被加载一直到程序结束
  3. 存储机制:定义的变量、方法只有一份,存储在静态方法区中,该类的所有对象共享这一份内容
  4. 使用时最好直接使用类名调用,避免混淆
  5. 静态方法不能访问非静态属性方法,只能访问静态属性和方法
  6. 当使用某一个对象改变其修饰的变量的值时,所有的对象中的值都被改变

main函数

参数:String[] args 字符串数组,可以传参,传参方式:在终端java 类名 参数

代码块

  1. 普通代码块,在方法中写的代码块,用于限制生命周期
  2. 构造代码块,比构造方法先执行
  3. 静态代码块,最先执行,且多次new对象时只执行一次(第一次使用类时执行),用于项目中初始化只调用一次的数据
public class Test{
    public static void main(String[] args){
        {
            //普通代码块,限制生命周期
            int i = 2;
        }
        //此处无法使用i
    }
}
class Student{
    static{
        //静态代码块,最先执行,并且只在第一次生成对象时执行
    }
    {
        //构造代码块,比构造方法先执行,每生成一个新对象都执行一次
    }
    public Student(){}

}

对象数组

即数组中的每个值都是一个对象

继承

关键字extends

继承只能继承非私有的(private)
构造方法不能被继承,创建子类时,父类的构造方法会被调用,用于初始化数据

super关键字

  1. 子类中用于表示调用父类的属性
  2. 用于调用父类的方法,特别是子类的方法被重写后,调用父类原来的方法(super.方法名
  3. 实例化子类对象时,会先调用父类的构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super(...)来调用父类的构造方法,super只能用于子类构造方法的第一句
class Test{
    public static void main(String[] args){
        HomeDog homeDog = new HomeDog("旺财");
        homeDog.print();
    }
}

class Dog{
    protected String name;
    private int age;
    public Dog(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("Dog的构造方法");
    }
}
class HomeDog extends Dog{
    public HomeDog(String name){
        super(name,2);//只能放在第一句,用于调用父类的构造方法
        System.out.println("HomeDog的构造方法");
    }
    public print(){
        System.out.println(super.name);//与下面相同,此处super表示父类
        System.out.println(name);
    }
}

方法重写

  1. 发生在子类中,方法重写的两个方法返回值,方法名,参数列表必须完全一致
  2. 子类方法抛出的异常不能超过父类抛出的异常
  3. 子类方法的访问级别不能低于父类相应方法的访问级别
  4. private,static,final修饰的的方法不能被重写

final关键字

final表示最终的,即不可以再改变的

  1. 修饰的变量相当于常量,其值不能改变(通常定义一个常量类)
  2. 定义的方法只能被继承不能被重写
  3. 定义的类不能被继承
  4. 形参使用final修饰,传入的实参不能被更改

抽象类

1.使用关键字abstract定义抽象类和抽象方法,抽象方法没有实现
2.继承抽象类的具体类,必须实现所有抽象方法,抽象类继承抽象类则不需要
3.抽象类不能被实例化,不能用final修饰
4.抽象类可以有实现方法和属性、构造方法

abstract class People{
    public abstract void name();//抽象方法,只有声明,没有实现
}

接口

  1. 使用interface关键字
  2. 接口中只能定义常量抽象方法,JDK1.8以后可以有默认实现方法,也可以有静态方法

    public default 返回值 function(){实现内容}

  3. 接口可以继承多个接口(extends xxx,xxx)
  4. 具体类实现接口使用implements,一个类可以实现多个接口
  5. 抽象类实现接口可以不实现接口里的方法
  6. 接口里的方法没有访问权限修饰符时默认是public
  7. 接口不能有构造方法
  8. 接口不能被实例化
interface IEat{
    void eat();//相当于public abstract void eat()
    int NUM = 10;//10,相当于public static final int fianl NUM = 10
    public default void print(){
        System.out.println("eat");
    }
}
interface ISleep extends IEat{
    //继承
    void sleep();
}
class Girl implements ISleep{
    //要实现所有方法
}

多态

  1. 方法重载和重写
  2. 用父类的引用指向子类对象
  3. 当需要把父类强制转换为子类型引用时,为了防止报类型转换错误,java.lang.ClassCastException,先用关键字instanceof进行类型检查,用于判断对象是否是指定的类型(对象本身及父类以上都通过),是就返回true,否则返回false

    a instanceof className

public class Test7{
    public static void main(String[] args) {
        Chicken yc = new YeChicken("小鸡");//用父类的引用指向子类对象(大类型表示小类型),自动向上转型
        Chicken hc = new HomeChicken("大鸡");
        Chicken wc = new WuChicken("乌鸡");
        yc.eat();
        hc.eat();
        yc = hc;//不会报错
        eat(yc);//若没有instanceof判断,会报类型转换错误,两个子类无法强制转换
        eat(wc);
    }
    //具体调用谁的eat看参数对象属于哪个类
    //面向抽象编程
    public static void eat(Chicken c){
        c.eat();
        if(c instanceof WuChicken){
            WuChicken wc = (WuChicken)c;//强制类型转换(向下转型)
            wc.song();
        }  
    }
}
abstract class Chicken{
    protected String name;
    public Chicken(){};
    public Chicken(String name){
        this.name = name;
    }
    public abstract void eat();
}
class YeChicken extends Chicken{
    public YeChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.print(name+"吃饭");
    }
}
class HomeChicken extends Chicken{
    public HomeChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.print(name+"吃饭");
    }
}
class WuChicken extends Chicken{
    public WuChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.print(name+"吃饭");
    }
    public void song(){
        System.out.print(name+"炖乌鸡");
    }
}

Object类

每个类都使用Object作为超类,所有对象(包括数组)都实现这个方法
所有类都是Object类的子类
常用方法:

  • public String toString():返回该对象名称的字符串表示,建议重写
  • public boolean equals(Object obj):比较两个对象的地址是否相等,根据情况可以重写
  • protected void finalize() throws Throwable:垃圾回收
  • public final Class<?>getClass():返回此Object的运行时类

内部类

在一个类的内部定义的类

class Outer{
    class Inner{}
}

编译时会产生两个文件

Outer.class和Outer$Inner.class

  1. 成员内部类:直接定义在类中
/*对内部类的访问*/
//需要通过外部类对象才能实例化内部类对象,因为类的非静态属性必须依赖于对象
//通常不建议这样做
Outer outer = new Outer();
Outer.Inner inner = outer.new.Inner();

//在外部类中定义一个方法,对外提供访问接口
class Outer{
    private String name;
    public void innerPrint(){
        Inner inner = new Inner();
        inner.print();
    }
    //一般把内部类设置为私有权限
    private class Inner{
        public void print(){
            System.out.println("inner");
        }
    }
}
//外部调用:
outer.innerPrint();
  1. 方法内部类:在一个方法中定义的类
  • 方法内部类只能在该方法中被实例化,不能被外部实例
  • 方法内部类对象只能使用该内部类所在方法的常量
//该方法在上面的类中
public void show(){
    (final) int x=10;
    class Inner2{
        public void print(){
            //x++; //不能改变变量的值,只能用
            System.out.println(x);
        }
    }
    Inner2 inner2 = new Inner2();
    inner2.print();
}
  1. 静态内部类:在一个类内部定义一个静态内部类:
  • 静态内部类仅能访问外部类的静态成员和方法
//外部调用
Outer.Inner3 inner3 = new Outer.Inner3();
inner3.print();

class Outer{
    private String name="Outer";
    static class Inner3{
        public void print(){
            System.out.println("静态内部类");
        }
    }
}
  1. 匿名内部类:没有名字的内部类
  • 不能有构造方法,只能有一个实例(一次性)
  • 不能有任何静态成员、方法
  • 没有权限操作符
  • 属于局部内部类

优先考虑静态内部类(无依赖),不会产生内存泄漏

//继承式匿名内部类
public void print1(){
    //虽然是实例化抽象类,但在实例化的同时实现内部抽象方法
    Cat cat = new Cat(){
        public void eat(){
            System.out.prinln("继承式匿名内部类");
        };//注意分号,本质上还是一条语句
    }
    cat.eat();
}
abstract class Cat(){
    public abstract void eat();
}

//接口式匿名内部类
public void print2(){
    Eat eat = new Eat(){
        public void eat(){
            System.out.println("接口式匿名内部类");
        }
    };
    eat.eat();
}
interface Eat(){
    public void eat();
}

//参数式匿名内部类
//main方法中
Outer outer = new Outer;
outer.print3(new Eat(){
    public void eat(){
        System.out.println("参数式匿名内部类");
    }
});
class Outer{
    public void print3(Eat eat){
        eat.eat();
    }
}

基本数据类型包装类

java对八种基本数据类型都进行了包装类
八种包装类分为两大类型:
Number:Integer、Short、Long、Double、Float、Byte是其子类
Object:Character、Boolean都是其子类
装箱及拆箱操作:

  • 将一个基本数据类型转换为包装类,称为装箱
  • 将一个包装类转换成基本数据类型,称为拆箱
Integer i1 = new Integer(10);//自动装箱
Integer i2 = 10;//与上面语句一样
int i3 = il.intValue();//自动拆箱
Interger i4 = new Integer("123");//自动将数值字符串转换成数值对象(只能是数值字符串)
int i5 = Integer.parseInt("123");//将数值字符串转换成数值
Integer i6 = Integer.valueOf("123");//将数值字符串转换成Integer对象实例

享元模式:使用共享对象,用来减少内存使用量,将大量重复对象变成一个共享的内存中,对于int类型数值,java默认一个字节的数值使用享元模式(同个地址)

//==比较的是地址,equals比较的是值
Integer x1 = new Integer(10);
Integer x2 = new Integer(10);
System.out.println(x1==x2);//false
System.out.prinlen(x1.equals(x2));//true

Integer x3 = new Integer(128);
Integer x4 = new Integer(128);
System.out.println(x3==x4);//false
System.out.println(x3.equals(x4));//true

Integer x5 = 10;
Integer x6 = 10;
System.out.println(x5==x6);//true(享元模式,共用一个地址上的常量)
System.out.println(x5.equals(x6));//true

Integer x7 = 128;
Integer x8 = 128;
System.out.println(x7==x8);//false(超出0-127一个字节,不被视为常用数,不使用享元模式)
System.out.println(x7.equals(x8));//ture

包与访问修饰符

包用于对多个java源文件管理,就如同目录
包的定义:(该语句只能出现在代码的第一句)

package com.vince
定义的包路径要与文件夹文件名、文件路径一致

异常处理

  • Throwable是所有异常的超类,分为Error和Exception,主要关注Exception
  • Exception分为编译期异常(受检)和运行期异常(非受检)
  • 开发中把可能出现异常的代码用try,catch语句包裹起来
  • 当代码出现异常时,会产生异常对象(jdk或者JVM产生)
  1. try、catch
  • catch可以有多个,顺序从子类到父类,范围广的放后面
  • finally(可选):作为程序的try部分的最终出口,不管最后有没有出错,都会执行
  • 测试时可以使用x.printStackTrace()打印出具体的异常信息
public class ExceptionDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        div(10,0);//运行时异常
    }
    public static void div(int num1,int num2) {
        int[] arr = new int[] {1,2,3,4,5};
        try{
            System.out.println(arr[5]);
            arr = null;
            System.out.println(arr.length);
            int result = num1/num2;
            System.out.println(result);
        }catch(ArithmeticException e) {
            System.out.println("除数不能为0");
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("数组下标越界");
        }catch(NullPointerException e) {
            System.out.println("空指针异常");
        }catch(Exception e){
            System.out.println("出错啦");
            //代码测试时打印出异常信息
            e.printStackTrace();
        }finally{
            //不管有没有出错,都会执行
            //若上面try的语句存在return语句,系统会自动搜索是是否存在finally,若存在,则先执行完finally的语句才执行return
            System.out.println("程序结束");
        }
    }
}

try,catch的另一种写法:

//try,catch的另一种写法:
try(...){
    ...
}catch(a|b e){
    ...
}
  1. throws、throw

throws主要用在方法的声明上,表示方法中不处理异常,交给调用处处理
throw手动抛出一个异常

public static int div(int a,int b) throws ArithmeticException{
    try{
        int c = a/b;
        return c;
    }catch(ArithmeticException e){
        throw new ArithmeticException("除数不能为零");//更改报错类型
    }
    //这里不需要return,因为程序已经因异常终止
}

常见异常:

  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • NullPointerException
  • InputMismatchException
  • ClassNotFoundException
  • ClassCastException
  • DataFormatException
  • RuntimeException

自定义异常

  • 通过继承Exception类的都是受检异常,继承RoutimeException都是非受检异常
  • 继承Exception都需要显式的使用try…catch…,继承RoutimeException的可以不调用,但通常仍要使用
  • 自定义异常的实现,通常是提供构造方法,异常对象本身没有实际功能
public class MyException extends Exception{
    public MyException() {
        super();
    }
    public MyException(String message) {
        super(message);
    }

assert关键字

表示断言,判断程序中某个变量是否是预期的结果

assert 语句

int result = 10+10;
assert result==20:"结果不正确";//如果结果不为20则报错

常用类库API

字符串操作 String类

  1. 两种赋值方式

    String name1 = “xiaoming”;
    String name2 = new String(“xiaomnig”);

存储机制:方法1在栈内存有指向堆内存的字符串常量池,方法2会先寻找常量池是否存在,若存在则只在堆内存创建一个对象,但若没有,会在堆内存创建一个对象,同时会在常量池创建一个字符串常量,name指向堆内存对象,而堆内存存储指向常量池的地址

String name3 = "xiaominig"
name1 == name2;//false
name1 == name3;//true
  1. 5种情况分析

变量的值只能在运行期才会被确定
如果在编译期可以确定,那么使用已有的对象,否则创建新的对象

(1) 情况1

String a = "a";//常量池编译期确定
String a1 = a+1;//运行期确定("a1")
String a2 = "a1";//常量池编译期确定
System.out.println(a1==a2);//false

(2) 情况2

final String b = "b";//final修饰的变量为静态,编译期确定
String b1 = b+"1";//编译期可以读取静态变量,编译期确定
String b2 = "b1";//编译期确定
System.out.println(b1==b2);//true

(3) 情况3

String c = getString();//通过方法取值,只能在运行期确定
String c1 = c+1;//运行期确定
String c2 = "c1";//编译期确定
System.out.println(c1==c2);//false

private static String getString(){
    return "c";
}

(4) 情况4

final String d = getString();//虽然是个final常量,但是方法还是在运行期才确定
String d1 = d+1;
String d2 = "d1";
System.out.println(d1==d2);//false

private static String getString(){
    return "d";
}

(5)情况5

String a = "a";
String b = "b";
String c = a+b+1;//运行时先产生a+b对象,再产生a+b+1对象
String d = "a"+"b"+1;//常量相加,只产生一个对象
  1. String类常用方法
  • public char charAt(int index):根据下标找到对应的字符
  • public char[] toCharArray():以字符数组的形式返回全部字符串内容
  • public String(char[] value):将全部的字符数组变为字符串
  • public String(char[] value,int offset,int count):将指定范围的字符数组变为字符串
  • public byte[] getBytes():将字符串变为字符数组
  • public String(byte[] bytes):将字节数组变为字符串
  • public String(byte[] bytes,int offset,int length):将指定范围的字节数组变为字符串
  • public String(byte[] bytes,String charsetName):通过指定的charset解码指定的byte数组,构造一个新的String
  • public boolean startsWith(String prefix):从第一个位置开始判断是否是以指定内容开头(prefix为正则表达式)
  • public boolean startsWith(String prefix,int tooffset):从指定的位置开始判断是否是以指定内容开头
  • public boolean endsWith(String suffix):判断是否是以指定内容结尾
  • public String replace(char oldChar,char newChar):替换指定字符
  • public String replace(CharSequence target,CharSequence replacement):替换指定字符串
  • public String replaceAll(String regex,Stirng replacement):替换指定字符串
  • public String replaceFirst(String regex,String replacement):替换第一个满足条件的字符串
  • public String substring(int beginIndex):从指定位置开始一直截取到末尾
  • public String substring(int beginIndex,int endIndex):截取指定范围的字符串
  • public String[] split(String regex):按照指定的字符串拆分
  • public String[] split(String regex,int limit):拆分字符串,并指定拆分的个数
  • public boolean contain(String s):返回一个字符串是否存在
  • public int indexOf(int ch):从头查找指定的字符是否存在,存在则返回位置,不存在返回-1;
  • public int indexOf(int ch,int fromIndex):从指定诶之查找字符串是否存在,存在返回位置,不存在返回-1;
  • public int inidexOf(String str):从头查找指定的字符串是否存在,存在返回位置,不存在返回-1
  • public int indexOf(String str,int fromIndex):从指定位置查找字符串是否存在,存在返回位置,不存在返回-1
  • public int lastIndexOf(int ch):从字符串的最后向前查找指定字符是否存在,存在返回位置,不存在返回-1
  • public int lastIndexOf(int ch,int fromIndex):从指定的末尾开始查找指定字符是否存在,存在返回位置,不存在返回-1
  • public int lastIndexOf(String str):从字符串的最后向前查找指定字符字符串是否存在,存在返回位置,不存在返回-1
  • public int lastIndexOf(String str,int fromIndex):从指定的末尾开始查找指定字符串是否存在,存在返回位置,不存在返回-1
  • public boolean isEmpty():判断内容是否为空
  • public int length():获取字符串的长度
  • public String toLowerCase():转小写
  • public String toUpperCase():转大写
  • public String trim():去掉开头和结尾的空格,中间的空格不去
  • public String concat():字符串连接操作

StringBuffer类

  1. 主要是用于解决字符串拼接产生多个对象的性能问题
  2. StringBuffer的内部实现采用字符串数组,默认数组长度是16,超过时采用原长*2+2的方式扩充
  3. 当预知添加的数据长度时,建议使用带初始化容量的构造方法,避免动态扩充影响性能
  4. 该类是线程安全的,性能低
//append返回的仍然是StringBuffer对象
public class StringBufferDemo {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        StringBuffer sb = new StringBuffer(17);
        sb.append(a).append(b).append(1);
        String d = sb.toString();
        System.out.println(d);
    }
}
  1. 常用方法:
  • public StringBuffer()
  • public StringBuffer(String str)
  • public StringBuffer(CharSequence seq)
  • public StringBuffer append(多种数据类型):字符串连接
  • public StringBuffer delete(int start,int end)
  • public int indexOf(String str)
  • public StringBuffer insert int(offset,多种数据类型)
  • public StringBuffer replace(int start,int end,String str)
  • publci String substring(int start,int end):截取指定范围字符串
  • public String substring(int start):字符串截取
  • public StringBuffer reserve():字符串反转

StringBuilder

线程不安全的,性能高,适合单线程使用
字符串相加在编译后会使用该类优化代码实现拼接

程序国际化

Locale类

创建一个本地语言环境对象,该对象会根据参数设置来自动选择与之相关的语言环境

public class Locale{
    public static void main(String[] args){
        //第一个参数表示语言,第二个参数表示地区
        Locale locale_CN = new Locale("zh","CN");
        Locale locale_US = new Locale("en","US");

        //使用默认方法会根据本地操作系统创建语言对象
        Locale locale_Default = Locale.getDefault();//静态方法
    }
}

ResourceBundle类

工具类不能用new
国际化通常的做法是将其定义成若干个属性文件(文件后缀为*.properties),属性文件中的格式采用”key=value”进行操作

info_en_US.properties
info_zh_CN.properties
ResourceBundle类表示的是一个资源文件的读取操作,所有资源文件需要使用该类进行读取,读取时不需要加上文件后缀

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Scanner;


public class InternationalDemo {
    public static void main(String[] args) {
        
        Locale locale_CN = new Locale("zh","CN");
        Locale locale_US = new Locale("en","US");
        
        Locale locale_defaul = Locale.getDefault();
        
        Scanner input = new Scanner(System.in);
        
        //通过工具类的静态方法getBundle获取文件信息
        //用于绑定属性文件的工具类(参数:属性文件的基本名)
        //不传第二个参数默认使用当前环境语言
        //可以传第二个参数指定语言版本
        ResourceBundle r = ResourceBundle.getBundle("info", );
        //getString()方法从属性中使用key来获取value值
        System.out.println(r.getString("system.name"));
        System.out.println(r.getString("input.username"));
        String username = input.nextLine();
        System.out.println(r.getString("input.password"));
        String password = input.nextLine();
        
        if("admin".equals(username) && "123".equals(password)) {
            System.out.println(r.getString("login.success"));
        }else {
            System.out.println(r.getString("login.error"));
        }
    }
}
//两个info文件

//info_zh_CN.properties
system.name = \u5458\u5DE5\u7BA1\u7406\u7CFB\u7EDF
input.username = \u8BF7\u8F93\u5165\u7528\u6237\u540D\uFF1A
input.password = \u8BF7\u8F93\u5165\u5BC6\u7801\uFF1A
login.success = \u767B\u5F55\u6210\u529F
login.error = \u767B\u5F55\u5931\u8D25

//info_en_US.propertises
system.name = EMP Manager System
input.username = Input UserName:
input.password = Input Password:
login.success = Login Success!
login.error = Login Error!

动态文本处理 java.text.MessageFormate类

该类继承于java.text.Format类,用于读取文本中不固定的量

//info_en_US.propertises
system.name = EMP Manager System
input.username = Input UserName:
input.password = Input Password:
login.success = Login Success!
login.error = Login Error!
welcome = welcome!{0}//不确定的量

String welcome = r.getString("welcome");
//第一个参数是文件中的key,第二个是该key中不确定量的value
welcome = MessageFormat.format(welcome, username);

Math类

数学运算方法,全是静态方法
使用Math类的两种方式:

  1. 直接使用(Math所在的java.lang为默认引入的包)
  2. 使用静态导入:import static java.lang.Math.方法名
    常用方法:
  • abs(double a):绝对值
  • random():返回double类型随机数
  • round(double a):返回最接近参数并等于某一整数的double值
  • sqrt(double a):平方根

Random类

用于生成伪随机数流
非静态类,使用前要先new一个对象

Random r = new Random();
常用方法:

  • nextLong():返回下一个long类型随机数
  • nextBoolean():返回一个随机boolean值
  • nextDouble(),nextFloat():返回一个对应类型的随机数,在0.0和1.0之间
  • nextInt(int n):参数可选,若无则范围为int的最大值,传参指定范围

Date类

表示特定的时间,精确到毫秒,也就是程序运行的当前时间

Date date = new Date();//当前时间
Date date = new Date(long data);//参数是毫秒

Calendar类

日历类,将时间精确到毫秒

//两种使用方法
Calendar c1 = Calendar.getInstance();
Calendar c2 = new GregorianCalendar();

int year = c1.get(Calendar.YEAR);
int month = c1.get(Calendar.MONTH);
int day = c1.get(Calendar.DAY_OF_MONTH);
int hour = c1.get(Calendat.HOUR_OF_DAY);
int minute = c1.get(Calendar.MINUTE);
int second = c1.get(Calendar.SECOND);
int millisecond = c1.get(Calendar.MILLISECOND);

DateFormate类

抽象类

DateFormate df = new SimpleDateFormate("yyyy年MM月dd日 HH:mm:ss SSS");
String nowDate = df.format(new Date());

对象比较器

Array.sort()方法可以实现排序,但是当类对象里有多个变量时,无法比较,此时需要使用对象比较器进行指定

  1. Comparable接口
    此接口强行对实现它的每个类进行整体排序,其中只有一个方法compareTo,被称为自然比较法(sort方法继承了这个接口),所以要用这个比较,就需要先在被比较的类中继承该接口并实现,否则无法强制类型转换
//Comparable是泛型,可以指定类
class Cat implements Comparable<Cat>{
    
    private String name;
    private int age;

    //省略构造方法

    //实现接口中的方法
    public int compareTo(Cat o){
        //该方法用于比较此对象与指定对象的顺序,如果该对象小于,等于,大于指定对象,则分别返回负整数,零或正整数
        return this.age-o.age;
    }
}

Cat[] cats = {new Cat("小a",2),new Cat("小b",1),new Cat("小c",6)};
//此时可以进行比较
Arrays.sort(cats);
  1. Comparator接口
    对于已经创建好的类,按照oo原则,对修改关闭,对扩展开放,所以可以使用Comparator接口重新定义一个新类来实现比较(该接口同样是泛型),该接口内有compare方法
public class CatComparator implements Comparator<Cat>{
    public int compare(Cat o1,Cat o2){
        return o1.getAge()-o2.getAge();
    }
}
//第一个参数是要比较的类,第二个是使用的比较器
Arrays.sort(cats,new CatComparator());

对象的克隆

在Object类中存在一个clone()方法:

protected Object clone() throws CloneNotSupportedException

被克隆的的对象需要实现Cloneable接口,该接口是标记接口,没有任何方法,用于告诉虚拟机该类需要克隆

public class CloneDemo implements Cloneable{
    //重写Object中的clone方法
    protected Object clone() throws CloneNotSupportedException{
        return super.clone;
    }
}

Cat cat = new Cat("xiao",2);
try{
    Cat newCat = (Cat)cat.clone();//因为返回的是object类型,所以要进行强制类型转换
}catch(CloneNotSupportedException e){
    e.printStackTrace();
}

克隆出来的对象因为是不同地址,所以不是同一个对象

System类

代表系统级的很多属性和控制方法

  1. 成员变量
  • in
  • out
  • error:标准错误输出流(红色字体)
  1. 成员方法
  • arraycopy():参数分别是:源数组,源数组的起始位置,要复制的数组,目标数组的起始位置,长度
int[] num = {1,2,3,4,5};
int[] num1 = new int[num.length];
System.arraycopy(num,0,num1,,0,num.length);
  • currentTimeMillis()返回当前计算机时间,时间为从1970年1月1日开始的毫秒数
  • exit(int status)退出JVM,其中status的值0表示表示正常退出,非零表示非正常退出
  • gc():回收垃圾,一般不用
  • getProperty(String key):获得系统中属性名为key的属性对应的值

Runtime类

使程序能够与其运行环境相连接

数字处理工具类

BigInter:对超过Integer范围的数据进行运算
BigDecimal:用于对小数点后多位的数据进行运算

  • 构造方法:

    public BigInter/BigDecimal(String val);

  • 常用方法:add,subtract,multiply,divide,remainder(加减乘除求余)

DecimalFormat:格式化数据

double pi = 3.1415926
long num = 2334534543

//取一位整数,3
new DecimalFormat("0").format(pi);
new DecimalFormat("#").format(pi);
//取一位整数和两位小数,3.14
new DecimalFormat("0.00").format(pi);
//取两位整数和三位小数,03.142
new DecimalFormat("00.000").format(pi);
//以百分比计数,并取两位小数 314.16%
new DecimalFormat("#.##%").format(pi);

//以规定格式233,453,4543
new DecimalFormat("###,###").format(num);

MD5工具类

  • 确认计算方法: MessageDigest md5 = MessageDigest.getInstance("md5");
  • 计算摘要:byte[] bytes = md5.digest(password.getBytes("UTF-8"));
  • 确认编码方式:String newStr = Base64.getEncode().encodeToString(bytes);
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class Md5Demo {
    private static String password = "admin123";
    private static String savePassword = "AZICOnu9cyUFFvBp3xi1AA==";// 最后编码后的字符串

    public static void main(String[] args) {
        //md5Test();
        System.out.println(login("admin123456"));
    }

    private static boolean login(String password) {
        if(savePassword.equals(md5Method(password))) {
            return true;
        }else {
            return false;
        }
    }
    
    private static String md5Method(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("md5");// 用于获取md5算法
            // 通过md5进行摘要
            byte[] bytes = md.digest(password.getBytes("UTF-8"));// 参数需要字节类型
            String mdStr = new String(bytes);// 此时若输出mdStr会出现乱码,因为已经被md5算法打乱
            // 对摘要进行编码
            // a-z,A-Z,0-9,/,* (BASE64编码算法)
            String str = Base64.getEncoder().encodeToString(bytes);
            return str;// AZICOnu9cyUFFvBp3xi1AA==
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    private static void md5Test() {
        String password = "admin123";
        String savePassword = "AZICOnu9cyUFFvBp3xi1AA==";// 最后编码后的字符串
        try {
            MessageDigest md = MessageDigest.getInstance("md5");// 用于获取md5算法
            // 通过md5进行摘要
            byte[] bytes = md.digest(password.getBytes("UTF-8"));// 参数需要字节类型
            String mdStr = new String(bytes);// 此时若输出mdStr会出现乱码,因为已经被md5算法打乱
            // 对摘要进行编码
            // a-z,A-Z,0-9,/,* (BASE64编码算法)
            String str = Base64.getEncoder().encodeToString(bytes);
            System.out.println(str);// AZICOnu9cyUFFvBp3xi1AA==
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

Lambda表达式

对于接口式匿名内部类,如果接口里只有一个方法,可以使用Lambda表达式替换

语法:(参数)->{具体方法}
当实现方法只有一条语句时可以省略大括号,有返回值且只有一条语句时直接写上返回值,如果参数需要使用修饰符,则必须完整写上所有修饰符

使用该表达式不会生成多余的class文件


public class LambdaDemo {
    public static void main(String[] args) {
        
        IEat1 ieat1 = ()->System.out.println("只有一条语句");
        ieat1.eat();
        
        IEat1 ieat2 = ()->{
            System.out.println("eat apples");
            System.out.println("多句话的写法");
        };
        ieat2.eat();
        
        IEat2 ieat3 = (name)->{System.out.println("有参数的方式"+name);};
        ieat3.eat("apple");
        
        IEat3 ieat4 = ()->0;
        System.out.println("相当于return 0 "+ieat4.eat());
        
        IEat4 ieat5 = (final String name)->System.out.println("要写修饰符必须写全");
        ieat5.eat("watermelon");
    }
}

interface IEat1{
    public void eat();
}

interface IEat2{
    public void eat(String name);
}

interface IEat3{
    public int eat();
}

interface IEat4{
    public void eat(final String name);
}