java基础1

一、数据类型

8个基本类型,这8个基本类型都有对应的包装类型,基本类型和对象的包装类型之间的赋值可以自动装箱和拆箱。

装箱就是 自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型。

比如 : Integer x=2;//装箱

         int y=x;       //拆箱

装箱和拆箱如何实现:

装箱调用包装器的ValueOf,拆箱调用xxxValue。xxx代表基本数据类型。

缓存池:

new Integer(123)和Integer.valueOf(123)的区别:

new Integer(123)每次会创建新对象。

Integer.valueOf(123)会使用缓存池中的对象,多次调用会取得同一个对象。

Integer z = Integer.valueOf(123);

Integer k = Integer.valueOf(123);

System.out.println(z == k); // true

问题:缓存池和常量池的区别?

valueOf()的实现就是如果有一样的就返回,没有再新建。

编译器在自动装箱的时候会调用valueOf,因此多个Integer实例使用自动装箱来创建并且值相同,就会引用相同的对象。

Integer m = 123;

Integer n = 123;

System.out.println(m == n); // true

使用基本类型的包装类型时候,就可以直接使用缓存池中的对象。

对于int,缓存池有-128到127的范围,所以一旦超出范围

Integer I=128;

Integer i2=128;==是false,因为都是new出来的。

    Double i1 = 100.0;

    Double i2 = 100.0;

    Double i3 = 200.0;

    Double i4 = 200.0;

都是false,因为double实现valueOf的方式与int不同。

:缓存池

https://www.cnblogs.com/Pjson/p/8777940.html

二、String

String是final,不可继承。不可变类是无法修改其实例的类。

Java 8 中String内存使用char数组来存储数据。

public final class String

implements [java.io](http://java.io/).Serializable, Comparable, CharSequence {

/** The value is used for character storage. */

private final char value[];

}

但是java9之后,String的实现使用byte数组存储字符串,同时使用coder来标识使用了哪种编码。

public final class String

implements [java.io](http://java.io/).Serializable, Comparable, CharSequence {

/** The value is used for character storage. */

private final byte[] value;

/** The identifier of the encoding used to encode the bytes in {@code value}. */

private final byte coder;

}

value数组被声明为final,这意味着value数组初始化之后就不能再引用其他数组,并且String内部没有改变value数组的方法,因此可以保证String不变。String为什么不能修改而只能改引用的原因。

为什么String不可变?

不可变的好处:

1.缓存HashCode,字符串的hash码在Java中经常使用,在HashMap或者HashSet中,不可变保证哈希码也不变的,意味着每次使用都不需要计算哈希码,效率高。

在String中有以下代码

private int hash;//缓存哈希码

String的hash值,HashMap咋用的?

2.String Pool的需要。如果一个String对象已经被创建过了,那么就会从String Pool直接引用,只有String不可变,String Pool才有意义。

3.安全性,String作为网络连接的参数时,被改变就不安全。

4.线程安全,String不可变性天生具备线程安全,可以在多个线程中安全使用。

5.促进其他类的使用

HashSet set = new HashSet();

set.add(new String("a"));

set.add(new String("b"));

set.add(new String("c"));

for(String a: set)

    a.value = "a";

如果String可变可能会违反set的设计,set包含非重复元素。

https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/网页内容:为什么String不可变?

此外,使用String进行逻辑操作相当慢,并且根本不建议,因为JVM将String转换为字节码中的StringBuffer。浪费了很多开销,从String转换为StringBuffer,然后再转换回String

什么事String的逻辑运算?

字符串常量池保存着所有字符串字面量(liter strings),这些字面量在编译时期就确定,不仅如此,还可以使用String的intern()方法在运行的过程中把字符串添加到String Pool。

当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。

什么是字面量:

String s5 = "bbb";

String s6 = "bbb";

System.out.println(s5 == s6); // true

“bbb”这种就是字面量,用上述形式会自动的把字符串放到String Pool里面。

String s1 = new String("aaa");

String s2 = new String("aaa");

System.out.println(s1 == s2); // false

String s3 = s1.intern();

String s4 = s1.intern();

System.out.println(s3 == s4); // true

上面这种,s1和s2是新建的两个对象,s1.intern()就是把s1引用的字符串放到String Pool里面,然后返回一个引用。当然,如果池里已经有了就不放了,直接返回。

在Java7之前,String Pool被放在运行时常量池,属于永久代,而在Java7,String Pool被移动到堆中,因为永久代空间有限,在大量字符串的场景下会导致OutOfMemoryError。

new String(“abc”)

使用这个会创建两个字符串对象(前提是String Pool里面没有“abc”字符串对象)

“abc”属于字符串字面量,因此在编译时期会在String Pool中创建一个字符串对象,指向这个“abc”。

而使用new会在堆中新建一个字符串对象。使用String Pool中的字符串对象作为String构造函数的参数。

String 构造函数的源码,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。

public String(String original) {

this.value = original.value;

this.hash = original.hash;

}

三、运算

java都是值传递。

float与double:

Java 不能隐式执行向下转型,因为这会使得精度降低。

1.1字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。

// float f = 1.1;

1.1f 字面量才是 float 类型。

float f = 1.1f;

隐式类型转换:

字面量1是int,所以不能隐式的将int向下转型为short。

short s1 = 1;

但是,使用+=或者++可以执行隐式类型转换

s1+=1;//相当于s1=(short)(s1+1);

switch不支持long类型,支持'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum’

四、继承:

访问权限应该尽可能让类的内部不被访问:

protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。

字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,

抽象类和接口:

抽象类一般会包含抽象方法,抽象方法一定位于抽象类中

接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。

从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。

接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。

接口的字段默认都是 static 和 final 的。

比较:

  • 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。

  • 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。

  • 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。

  • 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

搜索:什么是is-a,like-a

接口使用:

  • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;

  • 需要使用多重继承。

抽象类使用:

  • 需要在几个相关的类中共享代码。

  • 需要能控制继承来的成员的访问权限,而不是都为 public。

  • 需要继承非静态和非常量字段。

在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。

https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/

https://dzone.com/articles/when-to-use-abstract-class-and-intreface

super:

  • 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。

  • 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。

https://docs.oracle.com/javase/tutorial/java/IandI/super.html

重写和重载:

1.重写:

存在于继承体系中,子类实现了一个与父类在方法声明上完全相同的一个方法。

为了满足里氏替换原则,重写有以下两个限制:

子类方法的访问权限必须大于父类方法;

子类方法的返回类型必须上父类方法返回类型或子类型;

使用Override注解,可以让编译器检查是否满足上面两个条件。

2.重载:

在同一个类中,指一个方法与已经存在的方法名称相同,但是参数类型、个数、顺序至少有一个不同。

注意:返回值不同,其他都相同不是重载。

五、Object通用方法:

举几个例子:hashcode(),equals(),clone(),notify()等

equals()

1.等价关系

需要满足自反性,x.equals(x); // true

对称性 x.equals(y) == y.equals(x); // true

传递性 if (x.equals(y) && y.equals(z))

         x.equals(z); // true;

一致性 多次调用结果不变

3.实现:

1.判断是否为同一个对象的引用,如果是直接返回true

2.判断是否是同一个类型,如果不是,直接返回false

3.将Object对象进行转型,转换成实际类型

4.判断每个关键域是否相等

public class EqualExample {

private int x;

private int y;

private int z;

public EqualExample(int x, int y, int z) {

    this.x = x;

    this.y = y;

    this.z = z;

}

@Override

public boolean equals(Object o) {

    if (this == o) return true;

    if (o == null || getClass() != o.getClass()) return false;

    EqualExample that = (EqualExample) o;

    if (x != that.x) return false;

    if (y != that.y) return false;

    return z == that.z;

}

}

hashcode()

equals()用来判断两个对象是否等价,等价的两个对象散列值一定相等,但是散列值相等的两个对象不一定等价。

覆盖equals()的时候应该一起覆盖hashcode(),保证等价的两个对象散列值也相等。

如果你只覆盖了equals(),而不覆盖hashCode(),可能让两个相同的对象散列值不相同,

toString(),对象的toString方法

默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。

clone()

1.cloneable

clone()是Object的protected方法,它不是public,一个类不显式去重写clone(),就不能直接去调用该类实例的clone方法。

但是呢,如果你这样实现

public class CloneExample {

private int a;

private int b;

@Override

public CloneExample clone() throws CloneNotSupportedException {

    return (CloneExample)super.clone();

}

}

CloneExample e1 = new CloneExample();

try {

CloneExample e2 = e1.clone();

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

java.lang.CloneNotSupportedException: CloneExample

以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。

clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。

这样就对了

public class CloneExample implements Cloneable {

private int a;

private int b;

@Override

public Object clone() throws CloneNotSupportedException {

    return super.clone();

}

}

2.浅拷贝

拷贝对象和原始对象的引用类型引用同一个对象。

public class ShallowCloneExample implements Cloneable {

private int[] arr;

public ShallowCloneExample() {

    arr = new int[10];

    for (int i = 0; i < arr.length; i++) {

        arr[i] = i;

    }

}

public void set(int index, int value) {

    arr[index] = value;

}

public int get(int index) {

    return arr[index];

}

@Override

protected ShallowCloneExample clone() throws CloneNotSupportedException {

    return (ShallowCloneExample) super.clone();

}

}

ShallowCloneExample e1 = new ShallowCloneExample();

ShallowCloneExample e2 = null;

try {

e2 = e1.clone();

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

e1.set(2, 222);

System.out.println(e2.get(2)); // 222

3.深拷贝

拷贝对象和原始对象的恶引用类型引用不同的对象。

public class DeepCloneExample implements Cloneable {

private int[] arr;

public DeepCloneExample() {

    arr = new int[10];

    for (int i = 0; i < arr.length; i++) {

        arr[i] = i;

    }

}

public void set(int index, int value) {

    arr[index] = value;

}

public int get(int index) {

    return arr[index];

}

@Override

protected DeepCloneExample clone() throws CloneNotSupportedException {

    DeepCloneExample result = (DeepCloneExample) super.clone();

    result.arr = new int[arr.length];

    for (int i = 0; i < arr.length; i++) {

        result.arr[i] = arr[i];

    }

    return result;

}

}

DeepCloneExample e1 = new DeepCloneExample();

DeepCloneExample e2 = null;

try {

e2 = e1.clone();

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

e1.set(2, 222);

System.out.println(e2.get(2)); // 2

4.clone()的替代方案

使用clone()拷贝一个对象有风险,Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。看着是用构造函数来实现。

public class CloneConstructorExample {

private int[] arr;

public CloneConstructorExample() {

    arr = new int[10];

    for (int i = 0; i < arr.length; i++) {

        arr[i] = i;

    }

}

public CloneConstructorExample(CloneConstructorExample original) {

    arr = new int[original.arr.length];

    for (int i = 0; i < original.arr.length; i++) {

        arr[i] = original.arr[i];

    }

}

public void set(int index, int value) {

    arr[index] = value;

}

public int get(int index) {

    return arr[index];

}

}

CloneConstructorExample e1 = new CloneConstructorExample();

CloneConstructorExample e2 = new CloneConstructorExample(e1);

e1.set(2, 222);

System.out.println(e2.get(2)); // 2

六、关键字

static

1.静态变量,称为类变量,在内存中只有一个,所有实例用的都是同一个。

2.静态方法,静态方法在类加载的时候存在,不依赖于实例,所以静态方法必须有实现,所以说他不能是抽象方法。

静态方法只能访问所属类的静态字段和静态方法,方法中不能有this和super关键字。

3.静态语句块:静态语句块在类初始化时运行一次。

4.静态内部类

非静态内部类依赖于外部类的实例,而静态内部类不需要

public class OuterClass {

class InnerClass {

}

static class StaticInnerClass {

}

public static void main(String[] args) {

    // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context

    OuterClass outerClass = new OuterClass();

    InnerClass innerClass = outerClass.new InnerClass();

    StaticInnerClass staticInnerClass = new StaticInnerClass();

}

}

5.初始化顺序

静态变量和静态语句块又闲鱼实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。

public static String staticField = "静态变量”;

static {

System.out.println("静态语句块");

}

public String field = "实例变量”;

{

System.out.println("普通语句块");

}

最后才是构造函数的初始化

public InitialOrderTest() {

System.out.println("构造函数");

}

存在继承的情况下,初始化的顺序为:

  • 父类(静态变量、静态语句块)

  • 子类(静态变量、静态语句块)

  • 父类(实例变量、普通语句块)

  • 父类(构造函数)

  • 子类(实例变量、普通语句块)

  • 子类(构造函数)

七、反射

每个类都有一个class对象,包含了与类有关的信息,当编译个新类时,会产生一个同名的.class文件。

类加载相当于加载Class对象,类在第一次使用时才动态加载到JVM中。也可以使用Class.forName("com.mysql.jdbc.Driver”) 这种方式来控制加载,该方法会返回一个Class对象。

反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。

java基础1_第1张图片
image.png

http://www.importnew.com/7383.ht

类继承了之后,类成员也会被继承嘛?

补充:https://stackoverflow.com/questions/10578984/what-is-java-string-interning

https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value

https://stackoverflow.com/questions/9030817/differences-between-new-integer123-integer-valueof123-and-just-123

https://stackoverflow.com/questions/8710619/why-dont-javas-compound-assignment-operators-require-casting

https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java

你可能感兴趣的:(java基础1)