Java 基础

进制

1.进制转换

  1. 二进制转十进制
    从最低位开始将每个位上的数提取出来,乘以2的(位数-1)次方,然后相加
  2. 十进制转二进制
    将该数不断处于2,直到商为0为止,然后将每部得到的余数倒过来 ,就是二进制
  3. 二进制转8进制
    从低位起,将二进制数每三位 为一组,转成8进制即可 421
  4. 二进制转16 进制
    从低位起,将二进制数每四位 为一组,转成16进制即可 8421
  5. 八进制转二进制
    将八进制的每一位数,转成对应的一个三位数的二进制数

2.源码、反码、补码

  1. 对于正数,它的源码、反码、补码相同
  2. 负数的反码是将原码中除符号位以外的所有位(数值位)取反,也就是 0 变成 1,1 变成 0。
    1. 负数的补码是其反码加 1

3.位运算符

  1. 按位与&,运算规则:同1为一,其余为0。
  2. 按位或|,运算规则:两位又一个位1,结果位1,否则位0
  3. 按位异或^ ,两位一个为0,一个为1结果为1,否则为0.
  4. 按位取反~, 0变成1,1变成0.

数据类型

Java语言中的数据类型分为两大类,分别是基本类型和引用类型

基本数据类型

Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double

引用数据类

类(class)、接口、数

可变参数 (int...num)

​ ​ 在编写方法的过程中,可能会遇见一个方法有不确定参数个数的情况。一般我们会用方法重载来解决问题

 //方法重载,解决参数个数不确定问题
 public void method();
 public void method(int a);
 public void method(int a, int b);
 public void method(int a, int b, int c);

​ ​ 但是,当参数个数过多,这样太过于繁琐,于是我们可以使用不定项参数(可变参数)的方式:可以接受多个数据使用可变参数时候 当作数组来用

public class MethodReorganization {
    public static void main(String[] args) {

        Reorganization m= new Reorganization();
        int a=m.sum(1,2,3,4,5,6,7,8,8);
        System.out.println(a);
    }
}
//可以接受多个int的数据
//使用可变参数时候  当作数组来用
class  Reorganization{
    public int sum(int...nums){
        int res = 0;
        for (int i = 0;i<nums.length;i++){
            res += nums[i];
        }

        return res;
    }
    public int p(int a,int...b){ 
    //当有多个行参时候 可变参数放在最后 最多只能有一个可变参数
    return 0
    }
}

冒泡排序

public class maoPao {
    public static void main(String[] args) {
        int [] a={7,3,8,9,4,10};
        int tmp=0;
        for (int i = 0; i < a.length-1; i++) {
            for (int j = 0; j < a.length - 1; j++) {
                if(a[j]>a[j+1]){
                    tmp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=tmp;
                }
            }//显示过程
            for (int k = 0; k <a.length ; k++) {
                System.out.print(a[k]+"\t");
            }
            System.out.println();
        }
    }
}

构造器

​ ​ 构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现

  • 构造器没有返回值,也不能写void
  • 构造器的名称和类名(iConstructor)一样
  • (String Name,int Age)是构造器形参列表,规则和成员方法一样
public class Constructor {
    public static void main(String[] args) {

        iConstructor a=new iConstructor("jack",80);
        System.out.println("这是5 "+a.age+" "+a.name);
    }
}
class iConstructor{
    String name;
    int age;
    public iConstructor (String Name,int Age){
        name=Name;
        age =Age;
    }
}

对象创建的流程

This关键字

  • this关键学河以用来访问本类的属性、方法、构造器
  • this用于区分当前类的属性和局部变量
  • 访问成员方法的语法:this.方法名(参数列表);
  • 访问构造器语法:this(参数列表):注意只能在构造器中使用
  • .this不能在类定义的外部使用,只能在类定义的方法
public class This {
    public static void main(String[] args) {
      new excThis();

    }
}
class excThis{
    double aa;
    String name;
    public excThis(){
        this(12,"jack");
        // this     必须放在前面
        //访问构造器语法:this(参数列表)注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
        System.out.println("我是空格");
    }
    public excThis(double aa,String name){
        this.name=name;
        this.aa=aa;

        System.out.println(this.name);
    }

}

java 访问控制符

  1. 公开级别:用 pubslic修饰,对外公开
  2. 受保护级别用:protected修饰,对子类和同一个包中的类公开
  3. 默认级利: 设有修饰符号,中司一个包的类公开
  4. 私有级别:用 private修饰,只有类本身可以访问,不对外公开.
访问控制符范围
访问级别 访问修饰符 同类 同包 子类 不同包
公开 public 可以 可以 可以 可以
受保护的 protected 可以 可以 可以 不可以
默认权限 可以 可以 不可以 不可以
私有 private 可以 不可以 不可以 不可以
public class modifier {
    //四个属性,分别使用不同的访问修饰符来修饰
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private  int n4 = 400;
    public void m(){
        //该方法可以访间四个属性
        System.out.println("n1=" + n1 + " n2=" + n2 + "n3" + n3 + " n4=" + n4);
    }
}

Java 进阶

java 封装★

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

  1. 将属性进行私有化 private【不能直接修改属性】
  2. 提供一个公共的 (public)set方法,用于对属性判断井赋值
public void setXxx(参数名) {

   }
  1. 提供一个公共的 (public)get方法,用于获取属性的值
public 数据类型 getXxx() {
        return xxx;
    }

面向对象-继承(Extend)

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

Student.java

package tex;

public class Student {
    public int  math;
   private String name;
   protected int bb;

    public String getName() {//通过 get和set 方法来使用private属性
        return name;
    }

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

    public void aa(){
        System.out.println("我是aa");
    }
    private void bb(){
        System.out.println("我是bb");
    }
    protected void cc(){
        System.out.println("我是cc");

    }
    public void t(){//通过公共的方法使用 private
        bb();
    }
}

first.java

package tex;

public class first extends Student{
    public void f(){
        System.out.println(getName()+math+"\t");
    }
}
class  main{
    public static void main(String[] args) {
        first first = new first();
        first.setName("kunkun");
        first.math=12;
        first.f();
        first.aa();
        first.cc();
        first.t();

    }
  1. 子类必须调用父类的构造器,完成父类的初始化
  2. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无 参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super()去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过,++++当父类默认构造器被覆盖了。要使用 super()来指定++++
  3. 如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
  4. super()在使用时,需要放在构造器第一行 super 只能在构造器中使用,不能放在方法中使用
  5. super()this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  6. java所有类都是 Object类的子类,Object是所有类的基类

Super 关键字

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

  1. 访问父类的属性,但不能访问父类的 private属性 super.属性名
  2. 访问父类的方法,不能访问父类的 private方法 super.方法名(参数列表):
  3. 访问父类的构造器 super(参数列表);只能放在构造器的第一句,只能出现一句!

方法重写

​ ​ 子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖

  1. 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
  2. 子类方法的返回类型和父类返回类型一样
  3. 子类方法不能缩小父类方法的访问权限

方法重载

方法名相同,形参数据类型不同

public class Queue8 {
    public static void main(String[] args) {
       Overload a= new Overload();
       a.overload("goos");
       a.overload(2,3);
        System.out.println(a.overload("goos")+a.overload(2,3));
    }
}
class Overload {
    public double overload (int a,double b){
        return  a+b;
    }
    public String overload (String a){
        return  a;
    }
}

​ ​ 在一个Java类中,定义多个同名的方法,如果方法名相同,方法参数不同,包括参数的类型和个数都不同,叫做方法的重载。

方法重载和方法重写的区别

名称 发生范围 方法名 形参列表 返回类型 修饰符
方法重载 本类 一样 类型个数或者顺序至少有哟个不同 无需求 无要求
方法重写 父子类 一样 相同 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 子类方法不能缩小父类方法的访问范围

多态

​ ​ 同一种行为有多种表现形式,例如:同一件事件发生在不同的人身上有不同的结果

向上转型:子类对象转成父类对象。
向下转型:父类对象转为子类对象

编译类型 引用类型

​ ​ Java引用变量有两个类型,一个是编译时类型,还有一个是运行时类型。A a1 = new B() A是编译类型 B()是运行类型,当使用该对象引用进行调用的时候,有这么一条规则,对象调用编译时类型的属性和运行时类型的方法

    public class duotai {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.name);
        a.qq();

        System.out.println();

        A a1 = new B(); //向上转型
        System.out.println(a1.name);
        a1.qq();

        System.out.println();
        B b = new B();
        System.out.println(b.name);
        b.qq();
    }
}
class A{
    String name="dog";
    public void qq(){
        System.out.println("name is dog");
    }
}
class B extends A{
    String name="cat";
    public void qq(){
        System.out.println("name is cat");
    }
}

java的动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的 内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

多态的应用

多态数组

public class text {
    public static void main(String[] args) {
        Persion[] arr=new Persion[3];
        arr[0]=new Persion("jack",12);
        //向上转型 使用子类的say()方法
        arr[1]=new Student("nake",11,100);
        arr[2]=new Student("mary",12,99);

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i].say());
            if (arr[i] instanceof Student) {
                //向下转型  调用子类的方法
                ((Student)arr[i]).study();
                System.out.println();
            }

        }

    }
}
class Persion{
    private String name;
    private int age;

    public Persion(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String say(){
        return name +"\t"+age;
    }
}
class Student extends Persion{
    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }
    private double score;
    //重写父类的say方法
 
    @Override
    public String say() {
        return super.say()+"\tscore\t"+score;
    }
    //子类 特有的方法
    public void study(){
        System.out.printf("i am study");
    }
}

“==”和equals 的区别

  • “==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值
  • equals是Object的方法,比较的是所指向的对象的地址值,一般情况下,重写之后比较的是对象的值

  • “==”是运算符
    ①如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
    ②如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。
  • equals是Object的方法,用来比较两个对象的内容是否相等。
  • equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址;

类变量

  1. 什么时候需要用类变量

    当我们需要让某个类的所有对象都共享一个变量,就可以考虑使用类变量(靜态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, fee)

  2. 类变量与实例变量(普通属性)区别
    类变量是该类的所有对象共享的,而实例是每个对象独享的

public class text {
    public static void main(String[] args) {
        A make = new A("make");
        make.pay(100);
  
        A tom = new A("tom");
        tom.pay(300);
  
  
        System.out.println(A.fee);
        A.show();// 可以使用类名.static 方法调用

    }
}
class  A{
    public  String  name;
    public  static   double fee;//静态变量 fee
    public A(String name) {
        this.name = name;
    }
    public  void  pay(double fee){
        A.fee +=fee;
    }
    public static void show(){
        System.out.println(A.fee);
    }
}

静态变量和静态方法的使用场景

如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用)这时,把方法做成静态方法时非常合适

例如:system.out.println(“9开平方的结果是="+Math.sqrt(9)) 使用math的sqrt方法的时候 就可以直接调用。不用再new一个对象 然后再调用

代码块

基本语法

[修饰符]{//修饰符可选,要写的话,也只能写static
代码
};      //;号可以号上,也可以省略。

static 代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,井且只会执行一次。如果是普通代码块,每(new)创建一个对象就执行。

类什么时候被加载

  • 创建对象实例时(new)
  • 创建子类对象实例,父类也会被加载
  • 使用类的静态成员时(静态属性,静态方法)

创建一个对象时,在一个类调用顺序是

静态属性初始化、静态代码块->普通属性初始化、普通代码块->构造器


构造器的最前面其实隐含了super()和调用普通代码块,

class A {
	public A() {//构造器
//这里有隐藏的执行要求
//(1)super()
//(2)调用普通代码块和普通属性初始化
System.out.printin("ok");
 	}
}

我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:

  1. 父类的静态代码块和静态属性
  2. 子类的静态代码块和静态属性
  3. 父类的普通代码块和普通属性初始化
  4. 父类的构造方法
  5. 子类的普通代码块和普通属性初始化
  6. 子类的构造方法

例子:

//先进行类加载 先加载父类再加载子类
//创建对象后。先super()调用父类普通代码块和普通属性初始化  再调用本类的普通代码块和普通属性初始化
public class text{
    public static void main(String[] args) {
        new B();
    }
}
class A{
    public static int aa=aa();
    public int bb=bb();
    public A(){
        System.out.println("这是A的构造器");
    }
    static{
        System.out.println("这是A的静态代码块");
    }
    public static int aa(){
        System.out.println("这是A的静态属性初始化");
        return 0;
    }
    public int bb(){
        System.out.println("这是A的普通属性初始化");
        return 0;
    }
    {
        System.out.println("这是A的普通代码块");
    }
}
class B extends A{
    public static int cc=cc();
    public int dd=dd();
    public B(){
        //super();先加载父类
        //普通代码块
        System.out.println("这是B的构造器");
    }
    static{
        System.out.println("这是B的静态代码块");
    }
    public static int cc(){
        System.out.println("这是B的静态属性初始化");
        return 0;
    }
    public int dd(){
        System.out.println("这是B的普通属性初始化");
        return 0;
    }
    {
        System.out.println("这是B的普通代码块");
    }
}

运行结果:

这是A的静态属性初始化
这是A的静态代码块
这是B的静态属性初始化
这是B的静态代码块
这是A的普通属性初始化
这是A的普通代码块
这是A的构造器
这是B的普通属性初始化
这是B的普通代码块
这是B的构造器

final 关键字

final 可以修饰类、属性、方法和局部变量.

在某些情况下,程序员可能有以下需求,就会使用到final:

  1. 当不希望类被继承时,可以用final修饰
  2. 当不希望父类的某个方法被子类覆盖/重号(override)时,可以用final关键字修饰。
  3. 当不希望类的的某个属性的值被修改,可以用final修饰.
  4. 当不希望某个局部变量被修改,

抽象类(abdtract)

  1. 抽象类不能被实例化(new)
  2. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract 只能修饰类和方法,不能修属性
  5. 抽象方法不能使用private、final和static来修饰,

当有多介类,完成不同的任各job,要求统计得到各自完成任务的时间

抽象模版模式(提升代码的复用性)

public class t {
    public static void main(String[] args) {
        new b().calculateTime();
        new c().calculateTime();

    }

}
abstract class  a{//模版 公共的相的方法
    public  abstract  void  job();//抽象方法
    public void calculateTime(){
        long start= System.currentTimeMillis();
        job();
        long end = System.currentTimeMillis();
        System.out.println("运行时间"+(end-start));
    }
}
class b extends  a{//
    @Override
    public void job() {
        int num ;
        for (int i = 0; i < 9990000; i++) {
            num=+i;
        }
    }
}
class c extends a{//
    @Override
    public void job() {
        int num ;
        for (int i = 0; i < 10000; i++) {
            num=+i;
        }
    }
}

接口

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法号出来。

interface 接口名{  
  //属性
  //方法(1.抽象方法 2.默认方法 3.静态方法)
}

class 类名 implements 接口{
  //自己的方法
  //自己的属性
  //必须实现的方法
}
  • 接口不能被实例化
  • 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
  • 一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alttenter来解决
  • 抽象类去实现接口时,可以不实现接口的抽象方法
  • 接口中的属性,只能是final的,而且是 public static final修饰符

内部类

分类:

  • 局部内部类 -->是定义在外部类的局部位置,比如方法中,井且有类名。

    1. 可以直接访问外部类的所有成员,包含私有的
    2. 不能添加访问修饰符,但是可以使用final修饰
    3. 作用域:仅仅在定义它的方
    4. 局部内部类---访问-->外部类的成员【访问方式:直接访问】
    5. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访间外部类的成员,使用(外部类名.this.成员),去访问
  • 匿名内部类

    匿名内部类适合创建那种只需要一次使用的类

    new 类或接口(参数列表){
    //类体
    }
    

    从上面的定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。

  • 成员内部类

  • 静态内部类

枚举(enum)

Java 枚举是一个特殊的类,一般表示一组常量们必入一年四季,一年12个月份,一个星期的7天

Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

enum Color 
{ 
    RED, GREEN, BLUE; 
} 

异常处理

  • 执行过程中所发生的异常事件可分为两大类

    1. Error(错误):Java虛拟机无法解决的严重问题。如:JVM系统内部错误、资源 、耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error是严重错误,程序会崩溃。
    2. Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试恩读取不存在的文件,网络连接中 断等等,Exception分为两大类:运行时异常【程序运行时,发生的异常】和编译时异常【编程时,编泽器检查出的异常】
  • 异常包括

    1. NullPointerException 指针异常
    2. ArithmeticException 数学运算异常
    3. ArraylndexOutOfBoundsException 数组下标越界异常
    4. ClassCastException 类型转换异常
    5. NumberFormatException数
  • 异常处理的方式

    1. try-catch-finally

    程序员在代码中捕获发生的异常,自行处理

    1. throws

    将发生的异常抛出,交给调用者(方法)来处理,最顶级

当try代码块中有return时,会先把return的值放在一块临时空间中,再执行finally代码块中,最后返回return临时空间中存储的值。如果finnally中也有return语句则会更新return临时空间中的值。

public class Test {
    public static void main(String[] args) {
        System.out.println(test());

    }
    private static int test() {
        int temp = 1;
        try {
            System.out.println(temp); 1 
            return ++temp; 3
        } catch (Exception e) {
            System.out.println(temp);
            return ++temp;
        } finally {
            ++temp;
            System.out.println(temp);2
        }
    }
}
//

执行顺序为:

输出try里面的初始temp:1;

保存return里面temp的值:2;

执行finally的语句temp:3,输出temp:3;

返回try中的return语句,返回存在里面的temp的值:2; 输出temp:2。

常用类

包装类基本类型转换

  1. jdk5 前的手动装箱和拆箱方式,装箱:基本类型 -> 包装类型,反之,拆箱
  2. jdk5 以后(含jdk5)的自动装箱和拆箱方式
  3. 自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
public class Integer01 {
    //演示int <--> Integer 的装箱和拆箱
    //jdk5前是手动装箱和拆箱
    //手动装箱      int -> Integer
    int n1 = 100;
    Integer integer = new Integer(n1);
    Integer integer1 = Integer.valueOf(n1);

    //手动拆箱
    //Integer -> Int
     int i = integer.intValue();

     //jdk5后,就可以自动装箱和自动拆箱
    int n2 = 200;
    //自动装箱 int -> Integer
    Integer integer2 = n2;  //底层使用的是 Integer.valueOf(n2)
    //自动拆箱 Integer -> int
    int n3 = integer2;      //底层仍然使用的是 intValue()方法
}


基本类型和包装类型的区别

基本类型直接在栈中存储它的具体数值,而包装类型则存储的是堆中的引用,所以基本数据类型不需要new关键字,而包装类型需要new在堆中进行分配内存空间。

什么是拆箱

• 拆箱:将包装类类型转换为基本数据类型
• 拆箱调用Integer.intValue方法

什么是装箱

• 装箱:将基本数据类型转换为包装类类型
• 装箱调用的Integer.valueOf方法

自动装箱和自动拆箱

Java1.5 之前

Integer a = Integer.valueOf(100); //手动装箱
int b = a.intValue(); //手动拆箱

Java1.5 之后

Integer a = 100; //自动装箱
int b = a; //自动拆箱
  1. 基本类型和包装类型进行 == 比较,包装类型会自动拆箱,直接和基本类型比较值
int a = 9;
Integer b = 9;
System.out.println(a == b);

//上述代码的结果为 true。
  1. 当需要进行自动装箱时,如果数字在 -128 - 127之间,会直接使用缓存中的对象,而不是重新创建一个对象。
Integer A = 199;  
int a = A;   

执行第一句代码的时候,系统为我们执行了:
Integer A = Integer.valueOf(199);
执行第二句代码的时候,系统为我们执行了:
int a = A.intValue();

Integer a = 100;
Integer b = 100;
System.out.println(a == b);
//返回true
Integer a = 199;
Integer b = 199;
System.out.println(a == b);
//返回false

自动装箱是通过 Integer.valueOf() 完成的,看它的源码

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

IntegerCache 是什么,让我们一探究竟。

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

在Integer类中有一个静态内部类 IntegerCache,在 IntegerCache类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。

所以一开始的代码 Integer a = 100;Integer b = 100;System.out.println(a == b);的结果是 true,因为100 在-128~127范围之内,直接从缓存池中拿的。

Integer a = 199;Integer b = 199;System.out.println(a == b);的结果是 false,因为199 不在这个范围之内,所以 new 出来了两个 Integer 对象。既然是new出来的,那就会在堆空间中产生不同的对象,不同的对象在进行 ==比较的时候,比较的是 内存中的地址,不同对象的内存地址肯定不一样,所以返回false

Integer a = 9999;
Integer b = 9999;
System.out.println(a + 1 == b + 1);
//返回true

上述代码的运行结果是true,这是因为这2个包装类型的Integer在运算时会自动拆箱,变成2个基本数据类型的比较,相当于在判断

a.intValue() + 1 == b.intValue() + 1所以返回

stringBulider类

  1. StringBuilder和StringBuffer非常类似,均代表可変的字符序列,両目方法也一样
  2. tring:不可变字符序列,效率低,但是复用率高
  3. StringBuffer:可变字符序列、效率较高(增删)、线程安全
  4. StringBuilder:可変字符序列、效率最高、袋程不安全 如果是单线程可以使用

String Buffer 和String Builder使用的原则,结论:

  1. 如果字符串存在大量的修改操作,一般使用 String Buffer 式String Burlder
  2. 如果字符串存在大量的修改操作,井在単袋程的情況,使用 String Builder
  3. 如果字符串存在大量的修改操作,井在多浅程的情況,使用 String Buffer
  4. 如果我们字符串很少修改,被多多个对象引用了,使用String,比如配置信息等

java System 方法

  1. exit 退出程序
  2. 复制数组元素: System arraycopy(五个参数)——>(原数组,从原数组那个索引开始拷贝,目标数组,把原数组的数据拷贝到目标数组的索引,从原数组拷贝多少个数据到目标数组)
  3. System currentTimeMillens : 返回当前时间距离1970-1-1的毫秒数。

Collection接口和常用方法

  • Collection 接口实现类的特点

    Public interface Collection extends Iterable

  • collection实现于类可以存放多个元素,每个元素可以是Object

  • 有些 Collection的实现类,可以存放重复的元素,有些不可以

  • 有些Gollection的实现类,有些是有序的(List),有些不是有序(Set)

  • Collection接口没有直接的实现子类,是通过它的子接口set 和list来实现的

增强for和Iterator

用来遍历集合

for 的底层是使用 Iterator 来实现的

public class good {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        List arrayList = new ArrayList();
        arrayList.add(new Dog("Dog",1));
        arrayList.add(new Dog("Dof",2));
        arrayList.add(new Dog("Dod",3));
        for (Object o :arrayList) {
            System.out.println(o);
        }
        System.out.println("======================");
  
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
    }

}
class  Dog{
    private String name;
    private int  age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Arrylist 和Linkedlist的比较

地层结构 增删效率 该查效率
Arrylist 可变数组 较低 数组扩容 较高
Linkedlist 双向链表 较高,通过链表追加 较低

Hashset (实现set接口)

  • Set 接口对象存放对象数据是无序的(添加的顺序和取出的顺序不同)每次取出数据的顺序是固定的

  • Hashset 只能放一个null

  • Hashset 的底层是去实现HashMap 方法。

    1. 添加一个元素的时候,先得到Hash值->转化成索引
    2. 找到储存的位置table ,看这个索引位置是否已经存放元素,
    3. 没有直接加入,如果有调用equals方法比较
    4. 如果相同就放弃添加,如果没有这添加(挂载)到后面

TCP字节流编程

服务端

package ioRead;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class serverSide01 {
    public static void main(String[] args) throws Exception {
        // 服务端在10086端口监听
        ServerSocket serverSocket = new ServerSocket(10086);
        //等待连接
        Socket accept = serverSocket.accept();
        //读取客户端发送的数据
        //通过socket得到一个输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(accept.getInputStream());
        byte[] bytes = streamToByteArry(bufferedInputStream);
        //讲byte数据写入到指定文件
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("/Users/epoch/Downloads/IO/src/2.png"));
        bufferedOutputStream.write(bytes);


        //向客户端回复“收到图片”
        //通过socket获取到输出流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bufferedWriter.write("收到图片");
        bufferedWriter.flush();//把内容刷新到数据通道
        accept.shutdownOutput();//设置写入结束标记
        // 关闭资源
        bufferedWriter.close();
        bufferedOutputStream.close();
        bufferedInputStream.close();
        serverSocket.close();


    }

    public static byte[] streamToByteArry(InputStream inputStream) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len;
        while ((len = inputStream.read(bytes)) != -1) {
            byteArrayOutputStream.write(bytes, 0, len);
        }
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
        return byteArray;
    }
}

客户端

package ioRead;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class clientSide01 {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket(InetAddress.getLocalHost(), 10086);
        //创建读取文件输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new                       FileInputStream("/Users/epoch/Downloads/IO/src/1.png"));

        byte[] bytes = streamToByteArry(bufferedInputStream);
        //通过socket获取到输出流,讲byte数据发送给客户端
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        bufferedOutputStream.write(bytes);//将文件对应的字节文件数组的内容,写到数据通道
        bufferedInputStream.close();
        socket.shutdownOutput();//设置写入数据的结束标记

        //接收回复消息
        InputStream inputStream = socket.getInputStream();
        String s = streamToString(inputStream);
        System.out.println(s);
        //关闭相关的流
        inputStream.close();
        bufferedOutputStream.close();
        socket.close();

    }

    public static byte[] streamToByteArry(InputStream inputStream) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len;
        while ((len = inputStream.read(bytes)) != -1) {
            byteArrayOutputStream.write(bytes, 0, len);
        }
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
        return byteArray;
    }

   
}

UDP

  1. 编写一个接收端A,和一个发送端B
  2. 接收端A在 9999端口等待接收数据(receive)
  3. 发送端A向接收端B 发送 数据“hello,明天吃火锅~"
  4. 接收端B接收到 发送端A发送的数据,回复“好的,明天见”,
package UDP;
//serviceSide
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPReceiverA {
    public static void main(String[] args) throws IOException {
        //创建一个DatagramSocket对象,准备咋i0086端口接受收据
        DatagramSocket datagramSocket = new DatagramSocket(10086);
        //构建一个DatagramSocket对象,准备接收数据
        //一个数据包最大64k
        byte[] bytes = new byte[64 * 1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
        //接受数据 通过网络传输的DatagramSocket对象填充到packet对象
        datagramSocket.receive(datagramPacket);
        //packet 进行拆包取出数据,显示出来
        int length = datagramPacket.getLength();
        byte[] data = datagramPacket.getData();
        String s = new String(data, 0, length);
        System.out.println(s);
  
        //发送
        byte[] bytes1 = "hello,我是接送端".getBytes();
        datagramSocket.send(new DatagramPacket(bytes1, bytes1.length, InetAddress.getLocalHost(),25565));
        //关闭
        datagramSocket.close();
    }
}
package UDP;

import java.io.IOException;
import java.net.*;

public class UDPSenderB {
    public static void main(String[] args) throws IOException {
        //创建DatagramSocket  对象,准备接受数据
        DatagramSocket datagramSocket = new DatagramSocket(25565);
        //讲需要发送的数据封装到DatagramPacket 对象
        byte[] bytes = "hello,我是发送端".getBytes();
        //封装DatagramPacket 对象的data内容字节数组,data.length,主机端口
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),10086);
        //通过网络传输的DatagramSocket对象发出packet数据包
        datagramSocket.send(datagramPacket);

        datagramSocket.receive(new DatagramPacket("hello".getBytes(),"hello".getBytes().length));
        int length = datagramPacket.getLength();
        byte[] data = datagramPacket.getData();
        String s = new String(data, 0, length);
        System.out.println(s);


        datagramSocket.close();
    }
}