java基础(二)

Java基础二

其他

Equals & hashcode

  1. 为什么重写equals还要重写hashcode

    引入了hashCode()这个方法,只是为了提高equals效率

    1. 保证相同对象有相同的hashcode

      (在hashMap中 key相同 hashcode value 不同 (不会被覆盖)

      ​ key相同 hashcode value 相同 (会覆盖)

      (在hashSet中 同理 因为hashcode不同 导致(本来应该相同的对象不出现)结果出现了

    2. 两个对象hashCode相同 ,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)

    不重写的情况下,= 比较基本数据类型的值 、 equals 调用 hashcode 返回对象的地址 比较,

    重写情况equal下,比较方式发生了变化, equal 调用 hashcode 返回对象的地址比较 之后再比较对象内的成员变量的值是否相等,但是对象a 对象b 成员变量值相等,但是hashcode未重写的情况下还是直接判断内存地址,所以需要并重写hashcode。

  1. Object若不重写hashCode()的话,hashCode()如何计算出来的?

    hashCode 相当于key,value 对应 哈希值和散列码 散列码就是哈希函数根据哈希值和对象映射出来的唯一一个值,作为内存地址, 例如 数组 5 f(x) = y%5 数字是 6 则 存在 1的位置上,还有哈希冲突(线程探测、二次探测、伪随机探测、链地址发、 公共溢出区 )

  1. == 作用与基本的数据类型 作用于应用类型变量(比较指向对象内存地址)、 equals 不 作用于基本数据类型(没重写就是比较引用类型的变量所指向的对象的地址)

总结:

== 是比较基本数据类型的值 ,**数据类型和变量的值都一致时才返回true。**

如果是引用数据类型的话(不可变类String(字符常量池 字符相同有相同的地址 如果是连接 String连接后面的字符串值在编译时确定下来,不能直接引用常量池中的) Integer 等包装类已经重写equal和hashcode (Integer -127 128 有缓存机制 两个Integer才相等 ,如果Integer 和 int 会自动拆箱 两个int相等)

只有当它们指向同一个对象时才返回true。

1
2
3
4
5
String str1 = "a";
String str2 = "b";
String str3 = "ab";
string str4 = "a" + "b"; // str3 == str4 true string str4 = "ab";
string str5 = str1 + str2 // str5 != str3 str1 + str2 = new String("ab")

java8新特性

  1. Lambda表达式 &函数式接口

    Lambda表达式 函数作为参数 传递给某个方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Arrays.asList("a","b","c").forEach(e -> sout(e));

    //复杂语句块
    Arrays.asList("a","n","c").forEach( e ->{
    sout(e);
    sout(e);
    });
    //应用类成员和局部变量
    final String spearator = ",";
    Arrays.asList("a","b","c".forEach(
    (String e) -> sout(e + spearator);
    ));
    //返回值
    Arrays.asList("a","b","c").sort((e1,e2) -> e1.compareTo(e2));
    Arrays.asList("a","b").sort((e1,e2) ->{
    int result = e1.compareTo(e2);
    return result;
    })

    Lambda表达式良好兼容 -> 函数接口,(只有一个函数的接口) 该接口可以隐式转换Lambda表达式 默认方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //函数式接口
    @FunctionalInterface
    public interface Functional{
    void method();

    //默认方法
    // 高可用 该接口新增方法时 不需要在 已实现该接口的类也添加方法
    default void defaultMethod(){...};
    }

    方法引用 和 landba表达式紧密联系 -> java构造方法紧密 简洁

优点:简洁、易并行计算

缺点:可读性、不容易调试

数据类型

  1. 基本数据类型

    byte 1 short 2 int 4 long 8 | char 2 boolean float 4 double 8

    在这里插入图片描述封装类

    好处

  1. 应用数据类型

    ArrayList 、Vector、LinkedList ||| HashSet TreeSet ||| HashMap HashTable LinkedHashMap TreeMap

  2. 自动拆装箱

    1. 自动装箱

      1
      2
      Integer i = 100;
      Integer i = Integer.valueOf(100);
  1. 自动拆箱

    1
    2
    int one = i;
    int one = i.intValue();
  1. 在-128~127内的情况 (封装类有更多的重写方法)

    封装类的缓存机制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Integer i = 100;     Integer cache = new Integer(100)
    Integer b = 100; Integer i b = cache
    //( i == b ) == true;
    Integer a = 200; -相当> Integer a = new Integer(200)
    Integer b = 200; -相当> Integer b = new Integer(200)
    // (a == b) == false;
    在 -128 到 127 Integer.valueOf(int i )
    返回的是缓存对象 所以 i b 指的是同一个对象

    String s1 ="abc";
    String s2 ="abc"; s1 = s2 true
    String s3 = new String("abc");
    String s4 = new String("abc"); s3 != s4 true

值传递和引用传递

这个要和不可变类(没有 不允许) & 可变类挂钩(拥有改变自身的引用类型 类似setX)

  1. 基本类型和引用类型

    num是基本类型,变量直接保存值。

    str是引用类型,变量保存对象的地址

    在这里插入图片描述

  2. 赋值运算符(=)的作用

    1
    2
    num = 20;
    str = "java";

    num是基本类型, 改变变量的值

    str是引用类型,改变变量的地址

    在这里插入图片描述

  3. 调用方法 -> 赋值操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void foo(int value) {    基本类型
    value = 100; //num = 20
    } //value = 20
    //value = 100
    foo(num); // num 没有被改变
    没有提供改变自身方法的引用类型
    void foo(String text) { //str = 0x11"java"
    text = "windows"; //text= 0x11"java"
    //text= 0x12"windwos"
    }
    foo(str); // str 也没有被改变

    StringBuilder sb = new StringBuilder("iphone");
    void foo(StringBuilder builder) {
    builder.append("4"); 提供了改变自身方法的引用类型
    }
    foo(sb); // sb 被改变了,变成了"iphone4"。

    StringBuilder sb = new StringBuilder("iphone");
    void foo(StringBuilder builder) { 提供了改变自身方法的引用类型,
    但是不使用,而是使用赋值运算符。
    builder = new StringBuilder("ipad");
    }
    foo(sb); // sb 没有被改变,还是 "iphone"。

总结:

  1. 基本数据类型传值:形参不会修改实参。
  2. 引用类型传引用,形参实参指向同一个内存地址,所以对参数的修改会影响到实际的对象。
  3. String、Integer,Double 等Immutable(不可变类)特殊处理,可以理解为传值,最后的操作不会修改实参对象。

1567301589476

ArrayList & Array

  • Array:定长数组,长度不变
  • ArrayList:不定长数组,(相当数组的二次封装),加入泛型机制,存储基本数据类型的封装类(加入时会自动装箱) ArrayList 这样

Java Immutable机制

mutable immutable优缺点

mutable(引用) immutable(String+封装类)
优点 减少数据拷贝,效率高 为了保证数据不变,需要大量临时拷贝,浪费空间
缺点 内部数据会改变 线程安全,内部数据不会改变。常量池
值传递 引用传递

不可变特性主要为了满足常量池、线程安全、类加载的需求。合理使用不可变类可以带来极大的好处。

例如

StringBuilder

img

String

img

1
2
3
4
String s = "a";
s = s.concat("b"); // s += b;
StringBuilder sb = new StringBuilder("a");
sb.append("b");

构造Immutable类

  1. filed成员被private final修饰。
  2. 不提供setX
  3. 使用final修饰自定义类,确保该类不会被重写。
  4. 如果类中的成员为mutable类型,初始化该类或者get该成员时,使用深拷贝。确保类immutable。
1
2
3
4
5
6
7
public final class MyImmutable{
private final int[] myArray;
public MyImmutable(int[] array){
this.myArray = array.clone();
//而不是 this.myArray = array;
}
}

int 和 Integer 有什么区别

  1. int 基本数据结构 、 Integer 是包装类 ,包装类内有大量方法提供对该类型的数据进行操作,也对equals hashcode进行重写,Integer -127 128 有缓存机制, 所以 可以equals 但是 超过不行。包装类因此可以自动拆装箱,另外ArrayList这些容器因为不定长,支持泛型,所以只支持包装类。
  2. 两者都是不可变类,final类方法 没有set 拿去没有final需要clone ,线程安全,符合常量池、类加载需求,他们没有对应的引用传递
  3. 自动拆装箱 语法糖 java自动帮我们转换:装箱 Integer.valueOf() 拆箱:Integer.intValue()所以要注意无意识的拆装箱 (数据量大开销大)
  4. 值缓存(有范围)Integer i1 = 1; ||Integer i2 = Integer.valueOf(2); || Integer i3 = new Integer(3); 1 2 就用到值缓存,而三没有 (1 自动装箱 同二一致)String 默认都只会将绝对值较小的值放入缓存
  5. 线程不安全的:基本数据类型时线程不安全(封装类是不可变类 所以线程安全)AtomicInteger、AtomicLong

& &&

  • 对于:& – > 不管怎样,都会执行”&”符号左右两边的程序
  • 对于:&& – > 只有当符号”&&”左边程序为真(true)后,才会执行符号”&&”右边的程序。

Synchronize锁和lock

Synchronize种锁状态

  1. 锁当前对象:同步代码块、非静态同步函数(使用this锁)
  2. 锁当前类class:静态函数同步方法锁

区别 自动门 和 手动门

  1. synchronized java内置关键字、lock是java类
  2. 判断锁的状态
  3. 自动、手动释放锁(synchronize 执行完、异常 释放)(lock在finally中手工释放锁(unlock()方法释放锁)否则会死锁
  4. synchronize就是悲观锁、local就是乐观锁(尝试得到锁 得不到就不等了)
  5. synchronize可重入 不可中断、非公平 、lock 可重入 、可中断、可公平
  6. synchronize适合小量代码块 、lock适合大量同步代码块。

Volatile

  1. 可见性:被volatile 修饰的变量在被线程拿到后,如果a线程修改该变量,会通知内存修改相应的变量,这样其他线程拿取volatile就是a线程修改后的变量。
  2. 有序性:禁止指令重排,(指令重排是cpu为了提高执行效率,首先执行响应快的部分代码,而像byte[1024*1024]需要IO指令则会推后执行。
  3. 不能保证原子性:

Final关键字

自己理解:用在不可变类上面(String、Integer 等这些包装类、所以不能用set获取值,想拿到值就必须重新copy一份,因此也出现了StringBuilder这类,因此只能值引用,也应如此他线程安全符合常量池,值缓存、类加载等需求)

  1. 修饰区域:修饰类说明该类不能被继承,该类private的方法会被隐式指定为final方法,成员变量需另外指明。
  2. final成员变量:(局部成员变量懒汉式加载,使用前初始化即可)final必须在定义时构造器中初始化赋值不能再赋值
1
2
3
4
5
6
7
8
9
10
11
String a ="hello2";
final String b = "hello";
String c = "hello";
String e = b + 2; // b相当于编译期的常量的 "hello" + 2
//编译期间能确切知道final变量值的情况下
// final String b = gethello(); 这就不行
String f = "hello" + 2; // 同上
String g = c + 2; // String g = c.copy()+2
e = a true;
f = a true;
g != a true;

Wait

暂停线程会释放锁,wait方法的线程必须调用notify notifyAll才能唤醒。

String为啥不可变

我的理解:String被final修饰,String 的valueOf需要内部调用copy开辟新内存空间,满足常量池需求,线程安全

不可变: 对象不可动,那就只能复制其地址, 保证效率安全只是赋值一次。

  1. 常量池需求

    1567325522445

  2. 运行String对象缓存HashCode:字符串不变性保证了hash码的唯一性。

  3. 安全 : babalala

Object类

  1. clone()
  2. equals()
  3. getClass()
  4. hashCode()
  5. notify() notifyAll()
  6. toString()
  7. wait()

重载和重写

  • 重载只要考虑参数(类型和数量) 重写 太严格了。

Static

方便在没有创建对象情况下调用(方法、变量),不需要依赖对象来访问,只要类加载了,就可以通过类名来访问。

负载因子是2^n

哈希取模不如位移运算: hash%length==hash&(length-1) 而且length是2的幂。

0%