Java 两种核心机制

  • Java 虚拟机 (Java Virtal Machine)
  • 垃圾收集机制 (Garbage Collection)

核心机制-Java 虚拟机

  • JVM 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指
    令,管理数据、内存、寄存器 。
  • 对于不同的平台,有不同的虚拟机。只有某平台提供了对应的 java 虚拟机, java 程序才可在此平台运行
  • Java 虚拟机机制屏蔽了底层运行平台的差别,实现了一次编译,到处运行”

核心机制-垃圾回收

  • 不再使用的内存空间应回收垃圾回收。
  • 垃圾回收在 Java 程序运行过程中自动进行,程序员无法精确控制和干预。

JDK和JRE

JDK(Java开发工具包)

JDK是提供给 Java 开发人员使用的,其中包含了 java 的开发工具,也包括了JRE 。所以安装了 JDK ,就不用在单独安装 JRE 了。
其中的开发工具:编译工具 (javac.exe) 打包工具 (jar)等。

JRE(Java运行环境)

包括Java 虚拟机 (JVM Java Virtual Machine) 和 Java 程序所需的核心类库等,如果想要运行 一个开发好的 Java 程序,计算机中只需要安装 JRE 即可。

异常

Java 程序在执行过程中所发生的异常事件可分为两类:

  • Error:Java 虚拟机无法解决的严重问题 。 如: JVM 系统内部错误 、 资源耗尽等严重情况 。 比如:StackOverflowError 和 OOM一般 不编写针对性的代码进行处理。
  • Exception 其它因编程错误或偶然的外在因素导致的一般性问题可以使用针对性的代码进行处理 。 例如:
    • 空指针访问
    • 试图读取不存在的文件
    • 网络连接中断
    • 数组角标越界

运行时异常

是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。

编译时异常

是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。 编译器 要求 Java 程序必须捕获或声明所有编译时异常。

异常处理机制

机制一

try-catch-finally

机制二

throws语句,在方法声明中用 throws 语句可以声明抛出异常的列表 throws 后面的异常类型可以是方法中产生的异常类型也可以是它的父类。

手动抛出异常

  • 首先要生成异常类对象 然后通过 throw 语句实现抛出操作 提交给 Java 运行环境;

    1
    2
    IOException e=new IOException()
    throw e
  • 可以抛出的异常必须是 Throwable 或其子类的实例。

自定义异常类

用户自定义异常类MyException ,用于描述数据取值范围错误信息。用户自己的异常类必须继承现有的异常类。

多线程

并行:多 个 CPU 同时执行多个任务。比如:多个人同时做不同的事;

并发:一 个 CPU( 采用时间片 同时执行多个任务。比如:秒杀、多个人做同一件事。

线程的创建和启动

Java 语言的 JVM 允许程序运行多个线程,它通过 java.lang.Thread类来体现。

Thread类

构造器
  • Thread(): 创建新的 Thread 对象
  • Thread(String threadname): 创建线程并指定线程实例名
  • Thread( Runnable target) :指定创建线程的目标对象,它实现了 Runnable 接口中的 run 方法;
  • Thread(Runnable target, String name): 创建新的 Thread 对象

API中创建线程的两种方式

方式一: 继承 Thread 类
  • 定义子类继承 Thread 类。
  • 子类中重写 Thread 类中的 run 方法。
  • 创建 Thread 子类对象,即创建了线程对象。
  • 调用线程对象 start 方法:启动线程,调用 run 方法 。

注意:想要启动多线程,必须调用 start 方法 。手动调用run()不是多线程;且一个线程对象只能启动一次。

方式二:实现 Runnable 接口
  • 定义子类 ,实现 Runnable 接口。
  • 子类中重写 Runnable 接口中的 run 方法。
  • 通过 Thread 类含参构造器创建线程对象。
  • 将 Runnable 接口的子类对象作为实际参数 传递给 Thread 类的构造器中 。
  • 调用 Thread 类的 start 方法:开启线程,调用 Runnable 子类接口的 run 方法。
对比
  • 区别:
    • 继承Thread:线程代码存放 Thread 子类 run 方法中。
    • 实现 Runnable :线程代码存在接口的子类的 run 方法。
  • 实现 Runnable的好处:
    • 避免 了单继承的局限性多个线程可以共享同一个 接口实现类 的对象,非常适合多个相同线程来处理同一份资源。

相关方法

  • void start(): 启动线程,并执行对象的 run() 方法;
  • run(): 线程在被调度时执行的操作;
  • String getName(): 返回线程的名称;
  • void setName(String name) :设置该线程名称;
  • static Thread currentThread (): 返回 当前线程 。在 Thread 子类中就是 this ,通常用于主线程和 Runnable 实现类。
  • static void yield() :线程让步
    • 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
    • 若队列中没有同优先级的线程,忽略此方法
  • join() :当某个程序执行流中调用其他线程的 join() 方法时调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止;
    • 低优先级的线程也可以获得执行
  • static void sleep(long millis): 指定时间-毫秒
    • 令当前活动线程在指定时间段内放弃对 CPU 控制 使其他线程有机会被执行时间到后重排队。
  • stop(): 强制线程生命期结束,不推荐使用;
  • boolean isAlive(): 返回 boolean ,判断线程是否还活着。

线程的调度

  • 同优先级线程组成先进先出队列(先到先服务),使用时间片策略;
  • 对高优先级,使用优先调度的抢占式策略;

线程的优先级等级

  • MAX_PRIORITY:10
  • MIN_PRIORITY: 1
  • NORM_PRIORITY :5
  • getPriority(): 返回线程优先值;
  • setPriority(int newPriority ) 改变线程的优先级;

说明:线程创建时继承父线程的优先级,低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用。

声明周期

同步机制

同步代码块

1
2
3
synchronized(对象){
// 需要被同步的代码;
}

同步方法

1
2
3
public synchronized void show (String name){
//
}

注意:必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全。

释放锁

  • 当前线程的同步方法、同步代码块执行结束。
  • 当前线程在同步代码块、同步方法中遇到 break 、 return 终止了该代码块、该方法的继续执行。
  • 当前线程在同步代码块、同步方法中出现了未处理的 Error 或 Exception 导致异常结束。
  • 当前线程在同步代码块、同步方法中执行了线程对象的 wait() 方法,当前线程暂停,并释放锁。

死锁

  • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁;
  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续;

Lock(锁)

ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义, 在实现线程安全的控制中,比较常用的是ReentrantLock 可以显式加锁、释放锁 。

  • 使用 Lock 锁, JVM 将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类);

线程通信

  • wait():令当前线程挂起并放弃 CPU 、 同步资源并等待,使别的线程可访问并修改共享资源,而当前线程 排队 等候其他线程调用notify() 或 notifyAll() 方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。

  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待;

  • notifyAll():唤醒正在排队等待资源的所有线程结束等待;

    这三个方法只有在 synchronized 方法或 synchronized 代码块中才能使用,否则会报异常。

Java常用类

字符串相关

  • String类:代表字符串。 Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
    • String 是一个 final 类,代表不可变的字符序列;
    • 字符串常量存储在字符串常量池,目的是共享;字符串非常量对象存储在堆中
    • 常用方法:
      • int length() 返回字符串的长度;
      • char charAt(int index) 返回某索引处的字符;
      • boolean isEmpty() 判断是否是空字符串;
      • String toLowerCase() 使用默认语言环境 将 String 中的所有字符转换为小写
      • String toUpperCase() 使用默认语言环境 将 String 中的所有字符转换为大写
      • String trim() 返回字符串的副本 忽略前导空白和尾部空白;
      • boolean equals(Object obj) 比较字符串的内容是否相同;
      • String substring(int beginIndex, int endIndex) 返回一个新字符串 它是此字符串从 beginIndex 开始截取到 endIndex( 不包含)的一个子字符串 。
      • int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引;
      • int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引;
      • boolean contains(CharSequence s) 当且仅当此字符串包含指定的 char 值序列时,返回 true
      • String replace(CharSequence target, CharSequence replacement) 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串、
      • String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串 。
    • 字符串->基本数据类型、包装类
      • int parseInt (String s)
    • 基本 数据类型、包装类->字符串
      • public String valueOf int n)
  • StringBuffer类:可变字符序列、效率低、线程安全
    • StringBuffer append ( xxx):提供了很多的 append() 方法 用于进行字符串拼接
    • StringBuffer delete (int start,int end):删除指定位置的内容
    • StringBuffer replace (int start, int end, String str)str):把 [start,end) 位置替换为 str
    • StringBuffer insert (int offset, xxx)xxx):在指定位置插入 xxx
    • StringBuffer reverse ():把当前字符序列逆转
  • StringBuilder类:可变字符序列、效率高、 线程不安全

日期

java.util.Date 类

表示特定的瞬间,精确到毫秒,过时。

java.text.SimpleDateFormat类

  • SimpleDateFormat () :默认的模式和语言环境创建对象
  • public SimpleDateFormat (String pattern):该构造方法可以用参数指定的格式创建一个对象
  • public Date parse(String source) 从给定字符串的开始解析文本,以生成一个日期。

Java比较器

  • 自然排序: java.lang.Comparable
  • 定制排序: java.util.Comparator

Math 类

  • abs:绝对值
  • sqrt:平方根
  • pow(double a,doble b) : a的 b 次幂
  • max(double a,double b):最大值

BigInteger 类

  • BigInteger 可以表示不可变的任意精度的整数
  • BigInteger (String val) : 根据字符串构建 BigInteger 对象
  • BigInteger add (BigInteger val) :返回其值为 (this + val) 的 BigInteger
  • BigInteger subtract (BigInteger val) :返回其值为 (this val) 的 BigInteger
  • BigInteger multiply (BigInteger val) :返回其值为 (this * val) 的 BigInteger
  • BigInteger divide (BigInteger val) :返回其值为 (this / val) 的 BigInteger 。整数相除只保留整数部分 。
  • BigInteger remainder (BigInteger val) :返回其值为 (this % val) 的 BigInteger
  • BigInteger pow (int exponent) :返回其值为 (this^exponent ) 的 BigInteger 。

java.math.BigDecimal 类

  • 支持数字精度比较高
  • 与BigInteger 方法相似

枚举类

  • 枚举类对象的属性不应允许被改动 , 所以应该使用 private final 修饰
  • 使用 enum 定义的枚举类 默认继承 了 java.lang.Enum 类,因此不能再继承其他类
  • 枚举类的构造器只能使用 private 权限修饰符
  • 主要方法:
    • values() 方法 :返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
    • valueOf (String str )):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常
    • toString():返回当前枚举类对象常量的名称
    • 框架 = 注解 + 反射 + 设计模式。
  • 注解:Annotation
    • @author标明开发该类模块的作者 多个作者之间使用,分割
    • @param对方法中某参数的说明 如果没有参数就不能写
    • @return对方法返回值的说明 如果方法的返回值类型是 void 就不能写
    • Override: 限定重写父类方法 , 该注解只能用于方法

集合

使用 Array 存储对象方面具有 一些弊端 ,而 Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。

两种体系:

  • Collection 接口单列数据, 定义了存取一组对象的方法的集合
    • List 元素有序、可重复的集合
    • Set 元素无序、不可重复的集合
  • Map 接口: 双列数据,保存具有映射关系“ key-value 对”的集合

Collection接口方法:

  • add(Object obj):添加
  • void clear():清空集合;
  • boolean isEmpty():是否为空;
  • boolean contains(Object obj) 是通过元素的 equals 方法来判断是否是同一个对象;
  • boolean remove(Object obj) 通过 元素的 equals 方法判断是否是要删除的那个元素 。 只会删除找到的第一个元素
  • Object[] toArray():转换为对象数组
  • iterator():返回迭代器对象,用于集合遍历

Iterator接口的方法:

  • hasNext():boolean
  • next()
  • remove()
  • 在调用it.next 方法之前必须要调用 it.hasNext 进行检测。若不调用,且下一条记录无效,直接调用 it.next 会抛出NoSuchElementException 异常。
  • Iterator 可以删除集合的元素 但是是遍历过程中通过迭代器对象的 remove 方法不是集合对象的 remove 方法

List接口方法

  • void add( int index, Object ele 在 index 位置插入 ele 元素
  • Object get( int index): 获取指定 index 位置的元素
  • Object remove( int index): 移除指定 index 位置的元素,并返回此元素
  • Object set( int index, Object ele 设置指定 index 位置的元素为 ele
  • ArrayList:本质上, ArrayList 是对象引用的 一个 变长数组
  • LinkedList:双向链表,对频繁的插入或删除元素 的操作,建议使用 LinkedList 类,效率较高
    • void addFirst (Object obj)
    • Object getFirst()
    • Object removeFirst()
  • Vector:Vector 是线程安全的,慢,尽量不使用

面试题

ArrayList 和 LinkedList 的 异同?

1
2
二者都线程不安全,相对线程安全的Vector ,执行效率高。
此外,ArrayList 是实现了基于动态数组的数据结构, LinkedList 基于链表的数据结构。对于随机访问 get 和 set ArrayList 觉得优于 LinkedList ,因为 LinkedList 要移动指针。对于新增和删除 操作 add( 特指插入 和 remove LinkedList 比较占优势,因为 ArrayList 要移动数据。

ArrayList 和 Vector 的区别?

1
Vector 和 ArrayList 几乎是完全相同的 唯一的区别在于 Vector 是同步类 ( synchronized),属于强同步类。因此开销就比 ArrayList 要大,访问要慢。正常情况下 大多数的 Java 程序员使用ArrayList 而不是 Vector, 因为同步完全可以由程序员自己来控制。 Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。 Vector 还有一个子类 Stack 。
  • Set接口
    • HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
    • HashSet 不是线程安全的
    • 对于存放在 Set 容器中的对象, 对应的类一定要重写 equals 和 hashCode(Object obj) 方法,以实现对象相等规则 。即: :“相等的对象必须具有相等的散列码 。
    • LinkedHashSet:
      • LinkedHashSet 是 HashSet 的子类
      • LinkedHashSet 插入性能略低于 HashSet 但在迭代访问 Set 里的全部元素时有很好的性能。

map接口

  • Map 中的 key 用 Set 来存放, 不允许重复
  • 其中, HashMap 是 Map 接口使用频率最高的实 类
  • Object put(Object key,Object value) value):将指定 key-value 添加到 或修改 当前 map 对象中
  • Object remove(Object key) key):移除指定 key 的 key-value 对,并返回 value
  • Object get(Object key) key):获取指定 key 对应的 value
  • boolean containsKey(Object key) key):是否包含指定的 key
  • int size():返回 map 中 key-value 对的个数
  • HashMap 的内部存储结构其实是 数组和链表的结合
  • HashMap 数组扩容之后 最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置 并放进去这就是 resize 。
    • 如果我们已经预知 HashMap 中元素的个数那么预设元素的个数能够有效的提高 HashMap 的性能
  • 负载因子的大小决定了 HashMap 的数据密度。负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长
    造成 查询或插入时的比较次数增多,性能会下降。

泛型

所谓泛型就是允许在定义类 、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型 。

  • 体会:使用泛型的主要优点是能够在编译时而不是在运行时检测错误。

  • 通配符?
    • List<?>是 List、 List
    • 读取 List<?> 的对象 list 中的元素时,永远是安全的,因为不管 list 的真实类型是什么,它包含的都是 Object
    • 写入 list 中的元素时,不行。因为我们不知道 c 的元素类型,我们不能向其中添加对象。null例外)

IO流

File类的使用

java.io.File 类: 文件和文件目录路径的抽象表示形式,与平台无关

常用构造器:

  • public File(String pathname):以pathname 为路径创建 File 对象,可以是 绝对路径或者相对路径
  • 路径分隔符:
    • windows 和 DOS 系统默认使用 “\”来表示
    • UNIX 和 URL 使用“/”来表示
  • public String getAbsolutePath() 获取绝对路径
  • public String getParent() 获取上层文件目录路径 。 若无返回 null
  • public boolean createNewFile():创建文件 。 若 文件存在则不创建 返回 false
  • public boolean mkdirs ()创建文件目录 。 如果上层文件目录不存在一并创建创建文件 。 若文件存在,则不创建,返回 false
  • public boolean delete() 删除 文件或者文件夹

文件流

缓冲流

向流中写入字节时,不会直接写到文件先写到缓冲区中,直到缓冲区写满BufferedOutputStream,才会把缓冲区中的数据一次性写到文件 里 。使用方法flush() 可以强制将缓冲区的内容全部写入输出流

反射机制

Reflection (反射)是 被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法 。

加载完类之后 在堆内存的方法区中就产生了一个 Class 类型的对象 一个类只有一个 Class 对象 这个对象就包含了完整的类的结构信息 。 我们可以通过这个对象看到类的结构 。 这个对象就像一面镜子 透过这个镜子看到类的结构 所以 我们形象的称之为:反射 。

  • java是静态语言,但可以使用反射机制等获得动态语言特性;

反射的应用:动态代理

  • 使用一个代理将对象包装起来 , 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。
  • Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。