2019 -- java基础
[TOC]
一、数据类型
基本类型
- byte
- char
- short
- int
- long
- double
- float
- boolean
包装类
- Byte、Character、Short、Integer、Long、Double、Float、Boolean
- ==所有的包装类都是final修饰的,为什么???==
- ==基本类型存在的意义是什么,为什么不直接用包装类,万事万物皆对象,为什么还要有基本数据类型==
- 基本数据类型除了Boolean以外都是可以相互转换的,但是由于各自占的空间大小不同,高精度转换为低精度时可能会丢失,而且他是直接通过位来截断的,可能会被转得面目全非,所以最好不要轻易进行降精度的转换
- int到Integer的转换
Integer a = 1;
int b = a;
// 包装类转基本数据类型
int b = a.valueOf(1);
int c = 1;
Integer d = c;
// 基本数据类型转包装类
Integer d = c.intValue(1);
- 空指针
Integer a = null;
int b = a; //抛出NullPointException
- 两个包装类引用的相等性
Integer a = 1;
Integer b = 1;
System.out.println(a==b); //true
Integer c = 1000;
Integer d = 1000;
System.out.println(c==d); //false
// 以下为Integer的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) // 判断实参是否在可缓存范围内,默认为[-128, 127]
return IntegerCache.cache[i + (-IntegerCache.low)]; // 如果在,则取出初始化的Integer对象
return new Integer(i); // 如果不在,则创建一个新的Integer对象
}
缓存池
这个缓冲池是说的对于基本数据类型的缓存池
new Integer(123)和Integer.valueOf(123)的区别
其实就是上面的问题里面提过,使用valueOf的时候可以去访问缓存池,这个缓存池的大小是-128-127,如果在这个范围内的话会直接去取缓存池中的对象就不用新建对象了,而new这个关键字总是会创建新对象
哪些基础数据类型拥有缓存池
boolean、byte、short、int、char,Boolean就不用说了就俩值所以都在缓存池里面,byte类型大小和缓存池大小一样都可以缓存,而short和int都是-128-127,char的这个看不太懂\u0000 to \u007F,估计也是一样的大小只是表达方式问题
二、String
简介
String被声明为final,因此他不可以被继承;java8中String通过char数组进行存储;java9中使用byte数据进行存储,同时使用char类型的coder参数来标识使用了哪种编码。
不可变的好处
- 可以缓存hash值
- String Pool需要:Sting对象被创建后会从String Pool中取得引用,只有String是不可变的,才可能使用String Pool
- 安全性:String作为参数可以保证参数不变,例如在作为网络连接参数的时候
- 线程安全:不存在线程安全问题,可以在多个线程中安全的使用
String,StringBuffer,StringBuilder
- String不可变,另外俩都可变,可变带来一些好处,如果一个字符串被修改的频率很高的时候可以节省创建新对象的开支
- 线程安全性
- String天生线程安全
- StringBuilder不是线程安全的
- StringBuffer是线程安全的,为此带来的是更高的资源消耗
String Pool
字符串常量池中保存着所有字符串字面量,这个所谓字面量就是指字符串字符本身被存放到了一个内存空间中,这些字面量在编译时期就确定了,此外也可以通过String的intern()方法在运行过程中将字符串添加到常量池中。
intern()方法
关于这个intern()方法,当调用它的时候它去常量池中检查是否存在了相同的字面量,存在就直接返回这个字面量对象的引用;否则会再常量池中新建一个字面量对象然后返回它的引用。
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); //false
String s3 = s1.intern();
String s4 = s2.intern();
System.out.println(s3 == s4); //true
//如果是以下面的这种字面量的形式创建字符串,则会自动将字符串放入常量池中
String s = "aaa";
new String(“abc”)
经常会问说这个过程中有几个对象被创建了,两个或者一个,堆上一定会创建一个字符串对象,而常量池中就要看是否有相同的字符串对象存在了,不存在就创建一个,存在就直接引用。
再详细了说,在String Pool中不存在abc字面量的时候,会创建一个字符串对象,这是一个对象,在常量池中分配一块内存存储abc这个字面量,然后将常量池上创建的字符串对象指向这个字面量。再将这个常量池中的字符串对象作为String构造函数的参数在堆中创建一个字符串对象,这时并不是要复制一份常量池中对象内容而是将引用指向它。
String s1 = new String("he") + new String("llo");
String s2 = s1.intern();

三、运算
参数传递
java中只有值传递,没有引用传递
==将不同类型的参数进行方法调用后,原来的参数是否会发生改变==
float和double
这简直是大坑,总体上说两个类型都是浮点数,然后float属于的单精度,double是双精度,在内存上float占4个字节,double占用8个字节,float有效数字为8位(这里的有效数字是包含了小数点前的数字的,从小数点前向后计数),double有效数字为16位,通常来说cup处理单精度速度比处理双精度速度快;实际使用的时候好像很少会用到float
//float的定义
float f = 1.1; //会报错,默认1.1是double类型的
float f = (float) 1.1;
float f = 1.1f;
再看看java中浮点数的计算,结果很诡异
1 |
|
计算机中所有的数最终都要转换成二进制表示,当浮点数参与了计算,那么浮点数的二进制与十进制之间的转换过程会变得不可预知且不可逆;这里说一下既然float和double其实都是在表示小数,为啥不直接说他们叫小数还要说是浮点数呢,这些数都是以科学计数法的形式进行存储的,一个数50.534转换成科学计数法形式5.053e1,不太清楚什么是科学计数法,但它的小数点移动到了一个新的位置(浮动了),这么说起来浮点数是服务于科学计算的,不适合用来进行精确计算;
==问题来了:java中到底该如何进行小数运算==
隐式类型转换
1 | short s = 1; |
switch
枚举的关键字,枚举这个语法被用得好少,一般都直接使用判断语句了,其实还是有他的优点的,有机会试试;在java7之后可以在switch判断语句中使用String对象了
四、继承
抽象类和接口
抽象类
抽象类和方法都用abstract进行修饰,抽象类里面一般都有抽象方法,当然一定包含也可以,但是抽象方法必须在抽象类中才能定义;抽象类和普通类最大的区别是抽象类不能被实现,需要通过继承后去实例化其子类。
接口
接口是抽象类的延伸,在java8以前,可以把它看成是一个完全的抽象类,那时候它不能有任何的方法实现;但是从java8开始接口也可以有默认的方法实现了,因为完全抽象的维护成本太高了,在8之前要扩展一个接口方法时,就要修改所有实现了该接口的类;
接口的成员(方法和字段)都是public的,并且不允许被定义为private或者protected;
public interface Man {
int i = 0;
String name = "11";
default void show(String content){}
static void speak(){}
}
- ==接口的字段默认都是static和final的==
- 带来一些问题:接口是抽象类的延伸所以它也不能被实例化,所以它也没有构造函数,而字段又都是static final的,所以也不能留白,必须在定义的时候就实例化
- ==普通类里面static修饰的字段可以不初始化,final修饰的字段至少要在构造函数里面初始化,static final修饰的字段必须在定义的时候初始化==
抽象类和接口对比
- 本质来说一个是类只是有了一些约束而另一个是接口是一个完全的契约,而具体的不同其实是由于接口成员都是public所以没有权限管理,类可以继承多个接口而抽象类只能单继承
使用选择
其实大多数情况下都使用了接口,抽象类使用场景很少,一方面是抽象类严格的类层次要求,另一方面是java8之后接口可以默认实现方法后维护成本变低
super
==super大概就是一个父类的实例引用,对应this关键字是自身的一个实例引用==不知道为什么没把这俩哥们放到一起讲,然后super就是用来帮助访问父类的被重写了的方法(构造方法和普通方法都可以)的,不然被重写了就没法用了有的时候还是不科学的
重载和重写
重写(override)
在继承中子类定义了和父类中声明上完全相同的一个方法,而为了满足里式替换原则,最好是用@Override注解来让编译器帮忙检查是否满足这些条件:
- 访问权限
- 返回类型
- 抛出异常
重载(overload)
==方法名相同,参数类型、个数、顺序不同,不知道返回值类型不同算不算,而且返回值如果是父子类或者接口与实现关系时候怎么说==
五、Object通用方法
概览
public native int hashCode()
public boolean equals(Object obj)
protected native Object clone() throws CloneNotSupportedException
public String toString()
public final native Class<?> getClass()
protected void finalize() throws Throwable {}
public final native void notify()
public final native void notifyAll()
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
equals()
- 等价关系
//自反性
x.equals(x)
//对称性
x.equals(y) == y.equals(x); // true
//传递性
if (x.equals(y) && y.equals(z))
x.equals(z); // true;
//一致性:多次调用结果不变
x.equals(y) == x.equals(y); // true
//与null比较:这个才是重点,任何不是null的对象x调用equals(null)都是false,什么叫对象为null呢,就是对象被声明后没有被初始化
x.equals(null)
- 作用
- 与==的区别
- 对于基本类型: ==判断两个值是否相等,基本类型没有equals方法
- 对于非基本类型:equals判断值是否相等,==判断两个变量是否指向相同的对象
- ==判断是否是同一个对象的引用?不知道这里说的是什么意思==
- 与==的区别
hashCode()
返回hash值
toString()
clone()
==拷贝,内容挺多的,有时间看看==
六、关键字
final
变量
声明数据为常量,如果在定义的时候就赋值了那就是一个编译时常量,也可以是留白了等到运行时才赋值,赋值之后就不能改变
- 对基本类型,final使得数值不变
- 对于引用,final使得引用不能在改变指向的对象,只是相当于指针被锁死,但是引用的对象的内容是可以发生变化的
方法
声明方法的时候该方法不能被子类重写,其实private修饰方法的时候就是把方法指定为了final,这个时候当子类中再出现相同的方法签名也不会是重写,而是在子类中定义了一个新的方法
类
声明类不可以被继承,==什么时候需要说一个类不要被继承呢==
static
变量
叫做静态变量,意思说这个变量是类所有实例所共有的,静态变量在内存中只存储一份;别的变量都叫做实例变量,也就是说每个实例都会有一份,它与所在的实例是同生共死
方法
- 静态方法在类加载的时候就存在了,它不依赖与任何实例,所以静态方法必须有实现,导致它不能是抽象方法
- 只能访问所属类的静态方法和静态字段,因为别的字段不一定初始化了(==但是当年鸣哥说过另外一个概念,有点忘了==),方法中也不能有super和this关键字(这两哥们大概都是在类的对象初始化的时候才会生成的)
静态语句块
在类初始化的时候运行一次
静态内部类
非静态内部类需要依赖于外部类的实例,儿静态内部类不需要,同时它也不能去访问外部类的非静态的成员
初始化顺序
- 静态变量和静态语句块由于实例变量和普通语句块,而它俩的优先级是相同的也就是说谁在前面谁先执行
- 实例变量和普通语句块,这两哥们优先级也是一样的
- 构造函数
- 存在继承的情况下
- 父类(静态内容)
- 子类(静态内容)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
