Java总结(更新中)

2019-10-18  本文已影响0人  bps

Java基本类型

强类型语言
Type byte short int long double float
Size(Byte) 1 2 4 8 8 4
int hex = 0xCAFE;
int oct = 0777;
long l = 40000L;

如果 short byte 需要使用强制类型转换才能保持原类型
整数/0 Exception
非零浮点数/0 无穷大 Infinity
0.0/0 NaN (Not a number) 例如还有sqrt(-1)
使用BigDecimal计算没有误差
char类下是使用Unicode编码
boolean与int不能进行相互转换

浮点数计算误差问题

浮点运算存在误差,如2.0-1.1=8.89....
原因在于二进制小数无法精确的表达10进制小数,小数表示分为尾数,阶码


Java相关概念

Java applet 在网页上运行的Java程序 (被JavaScript,flash取代)
sdk jdk的早期版本,软件开发工具
JAR java Archive File java档案文件 一种兼容zip的压缩文件
jar cf test.jar test 创建jar包

不同版本的区别在于JavaAPI库内容

值传递和引用传递

值传递是对基本变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
引用传递是对对象型变量而言的,传递的是对象地址的一个副本。
一般认为Java内都是值传递,因为java传入的非基本类型值是引用。

深拷贝和浅拷贝

浅拷贝与深拷贝的区别

浅拷贝与深拷贝的区别在于前者复制不了引用类型的域

数组拷贝(深拷贝)
Array.copyOf(arr,length) 将arr的值拷贝一份出来,arrLength新数组长度
Object.clone 浅拷贝

关于克隆clone

自定义类实现克隆的方法:

实现标记接口Cloneable
实现Object clone()方法,通过(XX)super.clone()返回克隆对象

Object类提供的Clone机制只是简单的复制,存储的对象的域对象还是指向同一个地址。
(浅拷贝)只是克隆对象的成员变量值,不会对引用的对象进行克隆。
clone机制高效,比静态的copy方法快两倍。


动态绑定和静态绑定

静态绑定

程序运行前就知道方法所属了。

动态绑定

程序运行过程中,将函数调用和函数定义(绑定)对应起来。也可以说是找到方法是属于哪个类的。

在静态绑定中,绑定可以在运行时或编译时解析。
所有static,final和private方法的绑定过程都在编译时完成。


类型比较

Object的hashcode方法是本地方法,通过c或c++实现的,返回对象的内存地址。

对于HashMap中的key的比较,需要重写hashCode和equals两个方法。
在满足了hashcode值相等的前提下equals返回为true时,key才算相同。

Comparable接口和Comparator接口

comparable接口包含一个返回值为int的compareTo(T t)方法,常常表示这个类是可比较的,如包装类,枚举类。Arrays.binarySerach()就是需要一个实现Comparable接口的List,不然不能进行二分查询。
comparator接口包含两个抽象方法,分别是返回值为int的compara(T o1,T o2)方法和返回值为boolean的equals(Object obj)方法。因为每个类默认实现Object,所以equals可以选择性覆盖。这个接口称为比较器。描述的是比较策略,与对象(类)无关。但是本人想不通这个接口里设立这个equals方法的意义是什么。Collections.sort()这个容器排序方法就可以传入一个比较策略(策略设计模式)来比较集合元素。同理Collections.binarySearch方法也是。


继承和多态

Father one = new Son();
此处引用one是用其子类构造,但是被向上转型成父类的类型了
向下变型(父类转子类)类型转换会有个类型转换异常,因而常常使用(one instanceof Father)判断

多态

overload和override是Java多态性的不同表现,前者称为重载后者称为重写,覆盖。
overload可以让改变返回值的类型——这称为可协变的返回类型
多态可以是使用一个父类型(接口)可以初始化不同行为(不同派生类)的对象
多态可以是同一个方法拥有不同的类型

可协变的返回类型

子类方法覆盖父类方法,子类方法返回类型可以和父类方法不一致
但是返回类型限定为父类方法返回类型的子类型 参考<? extends Type>
因而这个限于引用类型,如果返回值是基本类型的话还是得保持一致的

内联

早期java中,如果一个方法没有被覆盖且很短,编译器会对其优化处理,即内联


接口和抽象类

声明方法而不去实现它的类被称为抽象类。
接口所有普通方法都不能自己实现,可以有default,statc方法(Java8)这些被实现的方法,
抽象类没有default关键字,有staic方法。
接口的域自动设为静态常量。
接口默认是default,一般设为public

接口和抽象类的区别

首先在意义上,接口是一种规范,定义了类的标准,而抽象类是类的高度抽象化。

用法上,接口的一般方法(defalut,static)都不能自己实现。抽象类可以有可实现的方法。
抽象类可以implements接口,继续抽象。抽象类的中的main入口可以被调用。

标记接口 没有方法,唯一目的是可以用instanceof进行类型检查。如xx instanceof Cloneable

abstract不能与final同时修饰一个类。


枚举类

enum XXEnum{ A,B,C,D;}

int compareTo(E o)和另一个枚举类实例比较
int ordinal()   返回枚举类实例的索引值
valueOf(enumType,Stirng) 返回指定名称的枚举值

内部类

内部类可以访问所在外部类的数据(这个概念叫闭包),包括私有。但对同一个包下其他类隐藏。
内部类有个隐式引用,指向创建它的外部类对象,一般是Outer.this.xx引用外部类的域
外部类访问内部类 this.new Inner();

普通类只有public和默认不修饰,内部类可以是static,protect,private
内部类拥有独立的名称空间,当外部类被其他类继承的时候,内部类没有被继承。

局部内部类

在局部作用域内的内部类,如方法体内。
不能被public、private,只能在局部内访问。
局部内部类可以访问局部变量,但是该局部变量必须是final的。可以不声明,但是这个局部变量的值一定不能改变。

匿名内部类

不命名直接创建类对象
常用定义实现某接口的类,如Comparator接口

静态内部类

为了单纯地隐藏该类到另一个类内部,并不需要内部类引用外部类。当然,外部类的静态域还是正常访问的。
static修饰类的话,只能是静态内部类。


异常类

Throwable(基类)
    Error       运行时系统内部错误
    Exception
        RuntimeException 程序错误导致的异常
            错误的类型转换、数组访问越界、访问空指针
        其他异常 程序本身没问题
            如I/O错误这些 文件结尾读取数据、打开错误格式的URL、用不存在的class查找Class错误

Error和RuntimeException称为为未检查异常。
其他为已检查异常,拥有异常处理器。
未检查异常要么不可控制(Error),要么应该避免发生(RuntimeException)。

堆栈跟踪

stack trace 一个方法调用的列表, 包含程序执行过程中方法调用的特定位置。

断言

声称(断言)某个东西是某东西(符合某个要求),若不是则抛出异常。


反射:获取Runtime类型信息的途径

常用功能:

程序运行期间,Java Runtime系统为所有对象维护一个runtime,保存着对象所属的类足迹。
虚拟机利用runtime信息选择相应的方法执行。

Class类用于访问这些信息

反射机制的内容

  1. 检查类的结构
    Field、Method、Constructor
    用以获取类的域,方法,构造器的相关信息。
    在java.lang.Class中,
    分别对应着getFields() getDeclaredFields getMethods()
  2. 查看编译时还不清楚的对象作用域
    反射机制的默认行为受限于java的访问控制权限。
    即private等静态域,通过getField.getName之类的访问会产生异常
  3. 使用反射编写泛型数组
    如,数组扩展,扩展的新数组需要确定类型。通过Array.newInstance

method.invoke(object this,Object...args)
第一个参数是对应对象句柄,静态方法可省略,可设置为null,第二个方法是对应方法参数,返回值是Object

getConstructor()方法获得构造器对象,配合newInstance()方法可以创建对象


泛型

泛型程序设计 generic programming
泛型类看作普通类的工厂。

泛型类
public class XX<T>{ 
    public XX(){} 
    public T getA(){};
    public void setA(T xx){};
}

/*普通类中的泛型方法*/
class XX{
    public <T> T getA(T[] xx){
        return xx[0];
    }
}
/*类型变量放在修饰符和返回类型之间

XX.<String>getA();这里返回一个String类型的值,其实String可以省略,因为编译器可以推断出所调用的方法。

泛型类型变量的限定

假如在一个泛型方法中,泛型变量的类型是限定的。
比如是实现某个接口的类型,这个类型的范围就缩小了,
这时候就需要在方法声明处修改,如:
public static <T extends Comparable> T fun(){..}
此时,这个T表示的是所有实现Comparable接口的类型,
限定多个事,用&隔开。如T extends Comparable & Serializable>

擦除:删除类型参数后的泛型类型名。

如XX<T>的原始类型是XX,类定义其中的T用Object替换
因为T是一个无限定的变量,所以直接用Object替换。
如果是<T extends Comparable & Serialiizable>, 则用Comparable替换

Pair<XX> xxs = ...;
XX xx = xx.getFirst();

这里getFirst()返回的XX被擦除成Object,编译器自动插入强制转换成XX.

类型变量的限定
/*此处泛型限定为实现所有Comparable接口的类型*/
    public static <T extends  Comparable> Pair<T> minMax(T[] a){
        if(a == null || a.length == 0) return null;
        T min = a[0];
        T max = a[0];
        for(int i = 0; i < a.length; i++){
            if(min.compareTo(a[i]) > 0) min = a[i];
            if(max.compareTo(a[i]) < 0) max = a[i];
        }
        return new Pair<T>(min,max);
    }
泛型类型的继承规则和通配符类型
<? extends Employee>  可能是Employee也可能是其派生类
<XX<? super Employee>  可能是Employee也可能是其超类

可以这么理解:这个容器存放的是所有实现Manager的类(包括自己)。我们不知道add的是什么类型,但是get方法却可以,因为我传出去的对象无论如何都能被Manager接收。可以add(null)

可以这么理解:这个通配符类型代表的是所有Manager的超类(包括自己)。要是传入一个Manager的派生类(Manager的超类不行),肯定都是能被Manager或者Manager的超类接收的,但是get方法却不可以,除了Object,其他都接收不了。

其他
    public static <T extends Throwable> void doWork(T t) throws T{
        try{
            do work
        }catch (Throwable realCause){
            t.initCause(realCause);
            throw t;
        }   
    }

集合框架

基本接口 Colllection,Map

迭代器

add(E)
previous() 对应next()方法
hasPrivious()

Set

通过实现Comparable<T>接口的compareTo方法来比较类的先后

如果要实现不同set实例不同的比较策略:
实现Comparator接口的compare(T a,T b)方法
然后将这个类的对象传给TreeSet的构造器,那么该TreeSet实例的的排序策略就定了
常常是通过匿名内部类实现,对应对象常常被称为函数对象。

List group = staff.subList(10,20)   [10,20)
group.clear()  清除子范围
SortedSet<E> subSet(E from, E to)
SortedSet<E> headSet(E to)
SOrtedSet<E> headSet(E from)  返回大于等于from,小于to的所有元素子集
SortedMap<K,V> subMap(K from,K to)
SortedMap<K,V> headMap(K to)
SortedMap<K,V> tailMap(K from)  返回键落在指定范围内的所有元素
Set<String> result = new HashSet<String>(a);
result.retainAll(b); //此时result便是ab的交集

视图:可以获得其他实现[集合接口和映射表接口]对象的对象 (可以结合数据库的视图理解)

例如keySet()返回的集合。 它是返回实现Set接口的类对象,这个类的方法对原映射表进行操作。
Array.asList(xx[]) 返回的对象不算ArrayList实例,而是一个视图对象,带有访问底层数组的get和set方法,改变数组大小的方法。

通过视图删除原映射表的内容
比如 view 为 map key的集合子范围,map.keySet().removeAll(view);

Map

Map接口有四个实现类,HashMap,HashTable,LinkedHashMap,TreeMap

Collections

Collections里有许多静态方法

Collections.sort(list)
Collections.sort(list,new Comparator);
Collections.sort(list, Collections.reverseOrder(new Comparator))   逆序排序

Java的排序,基本类型使用快排,引用类型使用归并排序,是一个稳定排序
先将元素转成数组并使用归并排序的变体进行排序,然后再复制回列表。

对于已排序的集合,可用Collections.binarySearch(容器,key/element) 也可以添加一个compartor对象的参数

    Collections.min、Collections.max
    Collectuons.copy(to,from)
    Collections.fill(con,value)
    Collections.addAll(con,valuel,value2...)
    Collections.replaceAll(con,oldValue,newValue)
其他

Java线程

创建线程
  1. 类继承Thread并实现run方法

注意不要实现Thread类或者Runnable对象的run方法。直接调用run方法不会启动新线程
应该使用Thread.start,将会创建一个执行run方法的新线程

  1. 类实现Runnable的run方法,并将类传给一个Thread对象 Thread(run)

创建来源于两个方法Thread() Thread(Runable target),前者通过继承,后者通过传参
Thread的源码中,只有含有Runnable类型的域,run方法才能执行,

void join() 若线程A调用线程B的join方法,那么线程的A的运行将会被暂停,直到B线程执行结束
static void yield() 尝试让当前线程暂停(让调度器重新调度)
中断线程

正常情况下,run方法执行到最后一条语句,线程将中止。
interrupt中断线程。 调用时线程的中断状态将被置位。
thread.isInterrupted()查看线程中断状态
Thread.interrrupted清除终端状态
若线程被阻塞则无法检测。检测时产生InterruptedException

线程状态

Thread.State getState()返回线程的状态

  1. new 线程刚刚创建时的状态
  2. Runable 调用了start方法,变为可运行状态,可能在运行也可能没用运行。
  3. Blocked 被阻塞 不运行任何代码消耗最少的资源 直到线程调度器重新激活它
    想要获得锁,而锁被其他线程持有,那么该线程进入阻塞状态。
  4. waiting 等待另一个线程通知调度器的状态
  5. Timed waiting 超时之后,计时等待
  6. Terminated 自然死亡或者意外死亡
线程优先级

setPriority(int)设置线程优先级,线程优先级是依赖于系统的。

守护线程

thread.setDaemon(true) 将线程设为守护线程,为其他线程提供服务,如计时线程,垃圾回收线程。
如果只有守护线程的话,那么程序也就结束了。

未捕获异常处理器

线程的run方法不能抛出任何被检测的异常,但是如果不检测的话会导致线程死亡。这时候可以:

  1. 安装一个处理器——一个实现Thread.UncaughtExceptionHandler接口uncaughtException方法的类。
  2. 用setUncaughtExceptionHandler方法为任何线程安装一个处理器
  3. 用Thread的静态方法setDefaultUncaughtExceptionHandler为所有线程安装一个默认处理器。

如果不安装默认处理器,默认处理器为空。
如果不为独立的线程安装处理器,此时的处理器就是该线程的ThreadGroup对象。
线程组是一个可以统一管理的线程集合,默认情况下所有线程属于同一个线程组。
ThradGroup实现了Thrad.UncaughtExceptionHandler接口

多线程和同步问题

concurrent 并发
parallel 并行 并发的极致

线程安全

一个类在单线程和多线程的情况下都能正常运行。

Java实现原子操作的两种方式
  1. Lock接口 保证一个共享变量一个时刻只能被一个线程访问。
  2. CAS

除了long,double之外的其他类型变量的写操作都是源自操作(JVM实现的)
用volatile修饰后可保证其原子性

用来保护代码片段,使得任何时候只有一个线程执行被保护的代码。
也可以管理视图进入被保护的代码片段的线程
通过条件对象来管理那些已经进入被保护的代码片段

按照JVM来分,分为

  1. 内部锁 又称监视器
  2. 显式锁
内部锁

使用synchronized修饰方法或者代码块,修饰的方法称为同步方法
用以保证该方法一次只被一个线程执行,而代码块称为同步块

synchronized(锁句柄){
...
}

这里的锁句柄可以是this,此时会锁句柄为锁,对应的句柄为引导的锁

显式锁

Lock接口的实例,默认实现类ReentrantLock
常用方法

void lock() 获取锁
void unlock() 释放锁  一般放在finally块里
boolean tryLock() 尝试获得锁
new ReentrantLock(true) 创建公平锁 (默认是非公平锁)公平锁增加了线程的暂停和唤

synchronized

java的每一个对象都有一个内部锁,一个方法用synchronized来声明,那么该对象的锁将保护整个方法。
对于静态同步方法,对应的便是类对象的内部锁。

public synchronized void method(){
    wait();
    ...
    notifyAll();
}

客户端锁定: 不推荐使用
synchronized(obj){...}线程获得obj的锁

线程安全

当多线程访问一个类时,可以不用考虑这些线程在运行时环境下的调度和交替执行,
并且不需要额外的同步及在调用方代码不必作其他的调度,这个类的行为仍然是正确的。

sleep方法和wait方法的联系和区别

await 释放锁并进入等待阻塞状态
signalAll 通知等待的线程,激活他们
若是一个线程进入await,而又没有其他等待的线程激活它,那么就进入了死锁

notify()    唤醒在此对象监视器上等待的单个线程。 
notifyAll() 唤醒在此对象监视器上等待的所有线程。
wait()      导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。

wait,notifyAll,notify都是final方法,来自Object
wait 方法使当前线程进入等待状态并释放锁

线程之间如何通信:通过notify和wait
什么要在同步块内呢:因为不同线程之间会随机竞争资源,我们要对共享资源的操作定序

垃圾回收

强制开始垃圾回收

System.gc()
Runtime.getRuntime().gc()

垃圾回收器调用finalize()
finalize()方法返回后,对象消失,垃圾回收机制开始执行。
可以重写finlize方法实现复活该被清理的对象。

强引用:一个对象赋给一个引用就是强引用,比如new一个对象,一个对象被赋值一个对象。
软引用:用SoftReference类实现,一般不会轻易回收,只有内存不够才会回收。
弱引用:用WeekReference类实现,一旦垃圾回收已启动,就会回收。
虚引用:不能单独存在,必须和引用队列联合使用。主要作用是跟踪对象被回收的状态。

String

不可变对象是指一个对象的状态在对象被创建之后就不再变化。
String是一个final类,String底层是char[] 实现的,实现时char[]是final的

不可变的好处:
节省堆空间。
不可变可保证安全性,比如数据库账户密码等,没有办法在不修改地址的情况下修改其值。
线程安全。因为不可变,不可写,读一致。
不可变保证了HashCode码的唯一性,不需要重新计算,适合作为字典的key

上一篇下一篇

猜你喜欢

热点阅读