Java(2)

文章目录

引用 转载 参考 CV

https://blog.csdn.net/hao19980724/article/details/83792516
https://www.jianshu.com/p/49d2c3975c56
https://www.cnblogs.com/chenhuan821361335/p/8507636.html
https://www.cnblogs.com/hen-java/p/12621052.html
https://zhuanlan.zhihu.com/p/62117994
https://blog.csdn.net/lsxf_xin/article/details/79712537
https://www.cnblogs.com/junrong624/p/11596191.html
https://www.cnblogs.com/shuaiguoguo/p/8883862.html
https://www.bilibili.com/video/BV1uJ411k7wy
https://blog.csdn.net/Zhang723670738/article/details/104133405
https://blog.csdn.net/Jason_LH1024/article/details/95458132
https://blog.csdn.net/Money_bagZ/article/details/105800694
https://www.cnblogs.com/hfumin/p/10134391.html
https://www.cnblogs.com/aademeng/articles/11230457.html
https://blog.csdn.net/qq_34626097/article/details/83020577
https://blog.csdn.net/zhao_miao/article/details/83245816
https://www.jianshu.com/p/0235759d8103
https://blog.csdn.net/m0_38061421/article/details/90020329
https://blog.csdn.net/qq_41950229/article/details/102168779
https://blog.csdn.net/weixin_44797490/article/details/91006241
https://blog.csdn.net/loulanyue_/article/details/100166717
https://blog.csdn.net/shendeguang/article/details/68942157
https://www.runoob.com/java/java8-streams.html

Java

基础知识 FAQ

JVM、JRE和JDK的区别:

JVM (Java Virtual Machine): java虚拟机,用于保证java的跨平台的特性。
JRE (Java Runtime Environment): java的运行环境,包括jvm+java的核心类库。
JDK (Java Development Kit): java的开发工具,包括jre+开发工具
java语言是跨平台,jvm不是跨平台的。

classpath 是什么

Classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。

标示符命名规则

由数字(0-9),大小写英文字母,以及_和$组成。
不能以数字开头。
不能使用关键字来自定义命名。

基本数据类型(4类8种)

	 整数类型:byte、short、int、long
	 浮点数类型:float、double
	 字符类型:char
	 布尔类型:boolean

(类型大小写注意,比如boolean是原始数据类型,Boolean是对应的封装对象)

类型位数最小值最大值默认值其他
byte8-128(-2^7)127(2^7-1)0有符号、二进制补码表示
short16-32768(-2^15)32767(2^15-1)0有符号、二进制补码表示
int32-2^312^31-10有符号、二进制补码表示
long64-2^632^63-10L(0l)有符号、二进制补码表示
float322^(-149)2^128-10.0f单精度、IEEE754标准
double642^(-1074)2^1024-10.0d双精度、IEEE754标准
char16\u0000(0)\uffff(65535)\u0000(0)单一的、Unicode字符
类型位数符号位指数位尾数位
float321823
double6411152

引用数据类型

	 类
	 接口
	 数组

类型转换

 精度从高到低  double  float  long  int  short(char)  byte 
 (1) 自动类型转换  将一个低精度 → 高精度 
 (2) 强制类型转换  将一个高精度 → 低精度(精度会下降)

java语言的三种技术架构

  • J2EE:企业版
    是为开发企业环境下的应用程序提供的一套解决方案。
    该技术体系中包含的技术如 Servlet、Jsp等,主要针对于Web应用程序开发。

  • J2SE:标准版
    是为开发普通桌面和商务应用程序提供的解决方案。
    该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。
    比如Java版的扫雷。

  • J2ME:小型版
    是为开发电子消费产品和嵌入式设备提供的解决方案。
    该技术体系主要应用于小型电子消费类产品,如手机中的应用程序等。

JVM内存结构 (重点)

  • 栈内存 (Stack) :用于存储局部变量,当数据使用完,所占空间会自动释放。

    • 方法的运行一定要在 栈 中运行
  • 堆内存 (Heap):数组和对象,通过new建立的实例都存放在堆内存中。

    • 凡是 new 出来的东西,都在堆当中
    • 堆中的 类成员方法存的是一个地址,其指向方法区中的方法
    • 堆内存中的东西都有一个地址值(16进制)
    • 堆内存中的数据都有默认值:
      • 整数 默认为 0
      • 浮点数 默认为0.0
      • 字符 默认我为 ‘\u0000’
      • 布尔 默认为 false
      • 引用类型 默认为 null
  • 方法区(method area):

    • 类成员方法
    • 静态成员、构造函数、常量池、线程池, 存贮 .class 相关信息,包含方法的信息
  • 本地方法区:window系统占用(操作系统相关)

  • 寄存器:与 CPU 有关

成员变量和局部变量的区别 (重点)

  • 定义的位置不一样

  • 作用域

    • 成员变量:针对整个类有效。
    • 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
  • 存储位置

    • 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
    • 局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。
  • 初始值

    • 成员变量:有默认初始值。
    • 局部变量:没有默认初始值,使用前必须赋值。
  • 生命周期

    • 成员变量:随着对象创建而诞生,随着对象被 GC 而消失
    • 局部变量:随着方法进栈而诞生,随着方法出栈而消失

匿名对象

  • 匿名对象就是没有名字的对象。是对象的一种简写形式。
  • 应用场景
    • 只调用一次类中的方法。
    • 可以作为实际参数在方法传递中使用

构造代码块:

 (1) 作用:给对象进行初始化,对象一建立就执行,而且优先于构造函数执行
 (2) 构造代码块和构造函数的区别:
			构造代码块是给所有不同对象的共性进行统一初始化
			构造函数是给对应的对象进行初始化

this关键字

 (1)this关键字代表本类对象的一个引用,谁调用this所在的方法,this就代表谁  
 (2)this的使用场景
	 A:用于区分同名成员变量和局部变量;
	 B:在定义函数时,该函数内部要用到调用该函数的对象时,因为此时对象还没建立,故this代表此对象
	 C:构造函数间调用
		 这个时候,this(参数)必须作为第一条语句存在。

Person p = new Person();在内存中做了哪些事情。

 (1)将Person.class文件加载进内存中。
 (2)如果p定义在主方法中,那么,就会在栈空间开辟一个变量空间p。
 (3)在堆内存给对象分配空间。
 (4)对对象中的成员进行默认初始化。
 (5)对对象中的成员进行显式初始化。
 (6)调用构造代码块对对象进行初始化。(如果没有就不执行)
 (7)调用构造方法对对象进行初始化。对象初始化完毕。
 (8)将对象的内存地址赋值给p变量,让p变量指向该对象。

static关键字

  • 用来修饰成员变量和成员函数
  • 静态的特点
    • 随着类的加载而加载,且只加载一次
    • 存储于一块固定的内存区域(方法区中的静态区),所以,可以直接被类名调用
    • 优先于对象存在
    • 对所有对象共享
    • 可以被类名直接调用
  • 静态的注意事项
    • 静态方法只能访问静态成员
      • 为什么:因为静态的内容是随着类的加载而加载,它是先进内存的。
    • 静态方法中不能使用this,super关键字
  • 主方法是静态的
    • public static void main(String[] args)
      • static:由于jvm调用main方法的时候,没有创建对象。只能通过类名调用。所以,main必须用static修饰。
      • void:由于main方法是被jvm调用,不需要返回值。用void修饰。

静态变量和成员变量的区别

  • 静态变量存储在 方法区中的静态区。
  • 静态的生命周期比较长,所以一般不推荐使用。

静态代码块

static {
	// 在这里写静态代码块的内容
}
  • 静态内容总是优先于非静态,所以静态代码块 比 构造方法先执行
  • 当第一次使用到含有静态代码块的类时, 静态代码块执行唯一的一次,它比main还先执行。
  • 执行顺序
    • 静态代码块 → 构造代码块 → 构造方法
  • 用途
    • 用来一次性地对静态成员变量进行赋值

制作API(次重点)

 API(全拼):Application Program Interface 应用程序编程接口。
 (1)类中的内容需要用文档注释。
 (2)使用JDK\bin目录下的javadoc工具。
	 格式:javadoc -d 目录 -author -version ArrayTool.java

有符号数据的表示法(次重点)

 原码,反码(原码取反),补码(反码+1)。

单例设计模式:

 (1)设计模式:
	 解决某类问题行之有效的方法,是一种思想,是规律的总结
 (2)用来保证某个类在内存中只有一个对象
 (3)保证唯一性的思想及步骤
	 为了避免其他程序建立该类对象,先禁止其他程序建立该类对象,即将构造函数私有化
	 为了其他程序访问到该类对象,须在本类中创建一个该类私有对象
	 为了方便其他程序访问到该类对象,可对外提供一个公共访问方式

单例设计模式的两种方式

  • 第 1 种:懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

public class LazySingleton
{
		private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
		private LazySingleton(){}    //private 避免类在外部被实例化
		public static synchronized LazySingleton getInstance()
		{
				//getInstance 方法前加同步
				if(instance==null)
				{
						instance=new LazySingleton();
				}
				return instance;
		}
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

  • 第 2 种:饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

public class HungrySingleton
{
		private static final HungrySingleton instance=new HungrySingleton();
		private HungrySingleton(){}
		public static HungrySingleton getInstance()
		{
				return instance;
		}
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

Math类的使用(重点)

Random类的使用(重点)

Scanner类的使用

继承(重点)

final关键字(重点)

  • 最终,不可变 的意思,可以用于修饰类,方法,局部变量,成员变量。
  • final修饰的类不能被继承。
  • final修饰的方法不能被重写。
  • final修饰的变量是一个常量。只能被赋值一次。
  • 内部类只能访问被final修饰的局部变量。

内部类(次重点)

  • 优点

    • 内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。
    • 内部类是另外一种封装,对外部的其他类隐藏。
    • 内部类可以实现java的单继承局限。
  • 缺点

    • 结构复杂。
  • 创建内部类对象

    • 在外部类外部 创建非静态内部类对象
      • Outer.Inner in = new Outer().new Inner(); (真的有两个 new)
    • 在外部类外部 创建静态内部类对象
      • Outer.Inner in = new Outer.Inner();
    • 在外部类内部创建内部类对象
      • 就像普通对象一样直接创建:Inner in = new Inner();
  • 分类

    • 成员内部类
      • 成员内部类内部不允许存在任何static变量或方法 正如成员方法中不能有任何静态属性 (成员方法与对象相关、静态属性与类有关)
      • 成员内部类是依附外部类的,只有创建了外部类才能创建内部类。
    • 静态内部类
      • 关键字static可以修饰成员变量、方法、代码块、其实还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,静态内部类和非静态内部类之间存在一个最大的区别,非静态内部类在编译完成之后会隐含的保存着一个引用,该引用是指向创建它的外围类,但是静态类没有。没有这个引用就意味着:
        • 静态内部类的创建不需要依赖外部类可以直接创建。
        • 静态内部类不可以使用任何外部类的非static类(包括属性和方法),但可以存在自己的成员变量。
    • 局部内部类(方法内部类)
      • 方法内部类不允许使用访问权限修饰符。public、private、protected均不允许。
      • 方法内部类不允许使用 static
      • 方法内部类对外部完全隐藏,除了创建这个类的方法可以访问它以外,其他地方均不能访问
      • 方法内部类如果想要使用方法形参,该形参必须使用final声明(JDK8形参变为隐式final声明)
    • 匿名内部类
      • 匿名内部类就是一个没有名字的方法内部类,因此特点和方法与方法内部类完全一致,除此之外,还有自己的特点:
        • 匿名内部类必须继承一个抽象类或者实现一个接口。
        • 匿名内部类没有类名,因此没有构造方法。
  • 访问特点

    • 内对外,随意访问;外对内,需要内部类对象
    • 内部类可以直接访问外部类中的成员,因为内部类持有外部类的引用,
      格式为:外部类名.this
    • 外部类要想访问内部类的成员,必须创建对象访问。
  • 什么使用时候内部类呢?
    假如有A类和B类,A类想直接访问B类的成员,B类访问A类成员的时候,
    需要创建A类对象进行访问,这个时候,就可以把A类定义为B类的内部类。

  • 通过class文件我们就可以区分是否带有内部类,以及内部类的位置

    • Outer$Inner.class :成员内部类
    • Outer$1Inner.class:局部内部类

内部类与外部类的关系

  • 对于非静态的内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。
  • 内部类可以直接访问外部类的元素(包括私有域)。外部类在内部类之前创建,创建内部类时会将外部类的对象传入
  • 外部类可以通过内部类的引用间接访问内部类元素 。即要想访问内部类属性,必须先创建内部类对象

为什么匿名内部类和局部内部类只能访问被final修饰的局部变量

根本原因是内部类的生命周期可能会超过局部变量的生命周期。局部变量的生命周期是当调用方法f()的时候,java虚拟机会将局部变量压入栈中(即局部变量产生),当方法f()运行结束后,java虚拟机就会将局部变量出栈(即局部变量销毁);但此时,内部类对象可能还存在,如果内部类对象此时去访问局部变量,那么就会出问题。解决办法就是把匿名内部类或局部内部类要访问的局部变量复制一份作为内部类对象的成员变量;这样当局部变量在栈中销毁以后,内部类还能访问局部变量。但是这样做会有一个问题,那就是必须时时刻刻保证复制的那一份局部变量的值和原来的局部变量的值相同,但是这样做是很困难的;于是,java中干脆就不允许内部类要访问的局部变量的值发生改变,也就是将这些局部变量用final修饰。(用final修饰的局部变量的值不能改变,只能是初始值。)

抽象类(重点)

 (1)多个类有相同的方法声明,但是方法体不一样。这个时候,我们考虑把方法声明进行抽取。
	让子类继承后,自己去实现方法体。没有方法体的方法,我们需要用抽象标志下。
	抽象的关键字是:abstract。
 (2)抽象类:
	 该方法称为抽象方法,包含抽象方法的类就是抽象类。
 (3)抽象类的特点:
	 A:抽象类和抽象方法都要用abstract进行修饰
	 B:抽象类不能被实例化
	 C:抽象类中不一定有抽象方法,但是,有抽象方法的类一定是抽象类。
 (4)抽象类中数据的特点
	 A:成员变量
		 抽象类中可以有变量,也可以有常量。
	 B:成员方法
		 抽象类中可以有抽象方法,也可以有非抽象方法。
	 C:构造方法
		 抽象类是一个类,所以,它有构造方法。
		 虽然本身不能实例化。但是可以给子类实例化使用。
 (5)抽象类中的问题
	 A:抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
		抽象类有构造方法。
			抽象类不能被实例化。
		抽象类中的构造方法供子类实例化调用。
	 B:抽象关键字abstract不可以和哪些关键字共存?
		private:
		 私有内容子类继承不到,所以,不能重写。
		 但是abstract修饰的方法,要求被重写。两者冲突。
			final
		 final修饰的方法不能被重写。
		 而abstract修饰的方法,要求被重写。两者冲突。       
		static
		 假如一个抽象方法能通过static修饰,那么这个方法,就可以直接通过类名调用。
		 而抽象方法是没有方法体的,这样的调用无意义。所以,不能用static修饰。
	 C:抽象类中可不可以没有抽象方法?如果可以,这样的类有什么用吗?
		抽象类可以没有抽象方法。
		抽象类中没有抽象方法的作用,只是为了不让别的类建立该抽象类对象。这个在awt中有体现。

接口interface

 (1)当一个类中的方法都是抽象的时候,java提供了另一种表示方式,叫接口。
	用interface关键字表示。类与接口关系用implements表示。
 (2)接口的成员特点
	 A:成员变量
		 是常量,默认修饰 public static final   
	 B:成员方法
		 都是抽象的,默认修饰 public abstract   
 (3)关系
	 A:类与类的关系
		 是继承关系。类与类只能单继承,可以多重继承。
	 B:类和接口的关系
		 是实现关系。类可以多实现接口。
		 类在继承一个类的同时,可以实现多个接口。
	 C:接口和接口的关系
		 是继承关系。接口可以多继承接口。
 (4)接口的特点
	 A:是对外暴露的规则
	 B:是功能的扩展
	 C:接口的出现降低耦合性。
		 耦合(类与类之间的关系)
		 内聚(类完成功能的能力)
		 编程规范:低耦合,高内聚。
	 D:接口可以多实现。如:CPU和主板、笔记本的USB插口、插座
 (5)接口和抽象类的区别
	 A:抽象类只能被单继承
		接口可以多实现,接口的出现避免了多继承的局限性。
	 B:抽象类中的数据特点:
			 成员变量:可以是变量,也可以是常量
			 成员方法:可以是抽象方法,也可以是非抽象方法
			 构造方法:有构造方法
		接口中的数据特点:
			 成员变量:是常量。默认修饰 public static final
			 成员方法:都是抽象方法。都有默认修饰 public abstract
			 构造方法:没有构造方法
	 C:抽象类中定义的是继承体系中的共性功能。
		接口中定义的是继承体系中的扩展功能。
	 D:抽象类被继承是"is a"关系:xx是yy的一种
		接口被实现是"like a"关系:xx像yy的一种

Object类

  • java.lang.Object 是所有类的根类,超类。java中提供的类以及我们自定义的类都直接或者间接的继承自Object类。

  • Object类中的方法

    • void finalize()
      当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
    • Class getClass()
      获取对象的字节码文件的描述类,后面再讲反射的时候还会在说这个类。
      String name = s.getClass().getName();
    • C:int hashCode()
      获取对象的哈希值。其实就是对象的内存地址值十进制表示
    • String toString()
      • 返回对象的字符串表示。
        默认表示格式:
        getClass().getName()+"@"+Integer.toHexString(hashCode());
      • 一般我们输出对象名的时候,其实底层调用的就是该对象的toString()方法。
        这种返回没有意义,所以,我们会重写这个方法,显示类的成员变量信息。
    • boolean equals(Object obj)
      • 用于比较两个对象的地址值是否相同。
      • 我们获取对象后,比较它的地址值意义不大。所以也会对这个方法进行重写。
        重写要完成什么功能,是根据需求定的。
  • ==和equals的用法:

    • ==怎么用?
      可以用于比较基本数据类型,比较的就是基本数据类型的值是否相等。
      可以用于比较引用数据类型,比较的是对象的地址值是否相等。
    • equals怎么用?
      equals只能用于比较引用数据类型的。
      Object提供的equals是用于比较对象地址值是否相同。
      自定义类中,如果重写了equals方法,那么就是按照你自己的需求来比较的。

Objects类

在JDK7版本的时候,Java引入了java.util.Objects工具类,用于封装一些平时使用频度很高或容易出错的操作,这些操作形成了Objects的各个方法,

  • Objects()构造方法
private Objects() {
    throw new AssertionError("No java.util.Objects instances for you!");
}

这个方法没有特别的地方,但有两点需要特别注意:

  • 该方法为私有方法

  • 其实现为抛出一个异常(或错误)

  • 其主要目的是为了避免对象的构造,即使通过反射机制也做不到!

  • equals()
    有别于Object.equals(),这个方法可以避免空指针异常。

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}
  • deepEquals()
    Object.equals()用于比较两个对象的引用是否相同,而deepEquals()却扩展成了可以支持数组。
 public static boolean deepEquals(Object a, Object b) {
    if (a == b)
        return true;
    else if (a == null || b == null)
        return false;
    else
        return Arrays.deepEquals0(a, b);
}
  • hashCode(Object o)
    和Object.hashCode()类似,只是在对象为null时返回的散列值为0而已。
public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}
  • hash()
    生成对象的散列值,包括数组。
public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
  • toString()
    归根结底,其内部最终调用了对象的toString()方法。只是额外多了空指针判断而已。
public static String toString(Object o) {
    return String.valueOf(o);
}

public static String toString(Object o, String nullDefault) {
    return (o != null) ? o.toString() : nullDefault;
}
  • compare()
    用于比较两个对象。
public static <T> int compare(T a, T b, Comparator<? super T> c) {
    return (a == b) ? 0 :  c.compare(a, b);
}
  • requireNonNull()
    在对象为空指针时,抛出特定message的空指针异常。
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}
public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
    if (obj == null)
        throw new NullPointerException(messageSupplier.get());
    return obj;
}
  • isNull() 和 nonNull()
    这两个方法用于判断对象为null和对象不为null。通常情况下,我们不会直接使用这两个方法,而是使用比较操作符==和!=。这两个方法主要用在jdk8开始支持的流计算里面。
public static boolean isNull(Object obj) {
    return obj == null;
}
public static boolean nonNull(Object obj) {
    return obj != null;
}

匿名内部类(局部内部类的简写) (重点)

(1)前提:继承一个类或者实现一个接口
(注意不要弄混匿名内部类的前提和多态的前提)
(2)格式:
new 父类名或者接口名()
{
重写父类方法或者实现接口中的方法。
也可以自定义其他方法。
};
(3)什么时候定义匿名内部类?
匿名内部类只是为了简化书写,匿名内部类有局限,通常定义匿名内部类时,该类方法不超过3个
(4)匿名内部类的好处和弊端:
好处:简化代码书写
弊端:
不能直接调用自己的特有方法
不能执行强转换动作
如果该类里面方法较多,不允许使用匿名内部类

java 异常体系

5982616-4ab25f2cfc5ca7b8.jpeg (C:\Users\windows\Desktop\Project\Markdown\img\java exception.jpg)

  • Throwable 类是 Java 语言中所有错误或异常的超类。
  • 只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。
  • Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。
  • 最后,它还可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。此 cause 设施在 1.4 版本中首次出现。它也称为异常链 设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。
  • Error

    • Error 是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获的严重问题
    • 大多数这样的错误都是异常条件。虽然 ThreadDeath 错误是一个“正规”的条件,但它也是 Error 的子类,因为大多数应用程序都不应该试图捕获它。
    • 在执行该方法期间,无需在其 throws 子句中声明可能抛出但是未能捕获的 Error 的任何子类,因为这些错误可能是再也不会发生的异常条件。
    • Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
  • Exception

    • Exception 异常主要分为两类
      • 一类是 IOException(I/O 输入输出异常),其中 IOException 及其子类异常又被称作「受查异常」
      • 另一类是 RuntimeException(运行时异常),RuntimeException 被称作「非受查异常」。
    • 受查异常就是指,编译器在编译期间要求必须得到处理的那些异常,你必须在编译期处理了。
  • 常见的非检查性异常:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3OVQ1WWk-1596683017887)(C:\Users\windows\Desktop\Project\Markdown\img\5982616-97fef7461bed33c6.webp)]

  • 常见的检查性异常:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2BgrAzR8-1596683017888)(C:\Users\windows\Desktop\Project\Markdown\img\5982616-ad31cf5b8e2cbb36.webp)]

  • 自定义异常类型

  • Java 的异常机制中所定义的所有异常不可能预见所有可能出现的错误,某些特定的情境下,则需要我们自定义异常类型来向上报告某些错误信息。

  • 在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

    • 所有异常都必须是 Throwable 的子类。
    • 如果希望写一个检查性异常类,则需要继承 Exception 类。
    • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
  • 异常的处理方式

    • try…catch关键字

      • 使用 try 和 catch 关键字可以捕获异常。
      • try/catch 代码块放在异常可能发生的地方。
      • try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
      try {
      	 // 程序代码
      } catch(ExceptionName e1) {
      	 //Catch 块
      }
      
      • Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
      • 一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获
      • 多重捕获块的语法如下所示:
      try{
      	// 程序代码
      }catch(异常类型1 异常的变量名1){
      	// 程序代码
      }catch(异常类型2 异常的变量名2){
      	// 程序代码
      }catch(异常类型2 异常的变量名2){
      	// 程序代码
      }
      
    • throws/throw 关键字

      • 如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
      • 下面方法的声明抛出一个 RemoteException 异常:
      public class className {
      	public void deposit(double amount) throws RemoteException {
      		// Method implementation
      		throw new RemoteException();
      	}
      	//Remainder of class definition
      }
      
      • 一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
    • finally关键字

      • finally 关键字用来创建在 try 代码块后面执行的代码块。
      • 无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
      • finally 代码块出现在 catch 代码块最后,语法如下:
      try{
      	// 程序代码
      }catch(异常类型1 异常的变量名1){
      	// 程序代码
      }catch(异常类型2 异常的变量名2){
      	// 程序代码
      }finally{
      	// 程序代码
      }
      

Java中String,stringbuilder,StringBuffer三者的区别

  • StringBuilder 的原理
    • 底层也是一个 byte数组,但没有被 final修饰,可以改变长度
    • byte[] value 初始长度为 16

1,首先说说运行速度,或者执行速度,三者的速度快慢大概为:stringbuilder>StringBuffer> String .

String 最常见,但最慢的原因:

String是字符串的常量,stringbuilder和StringBuffer是字符串变量,String一旦创建就不可更改,新的String其实是新的常量,String只能被创建不能被修改,stringbuilder和StringBuffer是可更改的,下面以代码为为例:

1 String str="abc";
2 Sys.out.println(str);
3 str=str+"de";
4 sys.out.println(str);

运行这段代码会先输出abc,在输出abcde,看起来这个str对象的值被改变了,其实是假象,第二个是创建了新的对象,两个对象不一样, 在jvm中代码是这样处理的,首先创建一个String对象str并把abc赋值给str,然后在第三行代码中,jvm其实又创建了一个新的对象也叫str,并把原来的str的值赋值和“de”赋值给新的str,而原来的str会被垃圾回收机制(GC)给回收掉了,所以str并没有被修改,也就是String一旦被创建后就不可更改,所以,Java中对String对象进行的操作实际上是一个不断地创建新的对象并且将旧的对象回收的过程,所以实行速度很慢。

而stringbuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不惊醒创建和回收操作,所以速度要比String快很多。

另外有时候我们会这样对字符串进行赋值

1 String str="abc"+"de";
2 Stringbuilder stringBuilder=new StringBuilder().append("abc")+append("de");
3 Sys.out.println(str);
4 Sys.out.println(stringBuilder);

这样输出结果同样是“abcde”和“abcde”,但是String的速度比StringBuild的执行速度快得多,因为第一行中的操作和String str=“abcde”;是完全一样,第一行只是一次创建对象并赋值, 而第二行是一次创建并且多次更改值,而如果写成下面这种形式

1 String str1="abc";
2 String str2="de";
3 String str=str1+str2;
4 Sys.out.println(str);

那么jvm就像上面所说的那样,不断的创建对象,回收对象来进行这个操作了,速度就会很慢。

2,再来说线程安全

在线程上,StringBuilder是线程不安全的,StringBuffer是线程安全的

如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但stringbuilder的方法没有关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作时多线程,那么就要用StringBuffer,但是在单线程的情况下,还是建议适用速度比较快的stringbuilder。

3,总结一下

String:适用于少量的字符串操作的情况,即创建复制后,修改的比较少的情况

StringBuffer:适用于单线程下字符串缓冲区进行大量操作的情况

StringBuilder:适用于多线程下字符串缓冲区进行大量操作的情况

String (重点)

  • 字符串的内容永不可变
  • 因为字符串的内容永不可变,所以字符串可以共享使用
  • 字符串效果上相当于 char[] 字符数组,但是底层原理是 byte [] 字节数组
  • 3种构造方法
    * public String()
    * public String( char [] array )
    * char [] array = {‘A’ , ‘B’ , ‘C’}
    * public String( byte [] array )
    * byte [] array = { 97 , 98 ,99}
  • 直接创建
    * String str = “Hello”

字符串的常量池 (重点)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uyoy9oo9-1596683017891)(C:\Users\windows\Desktop\Project\Markdown\img\java string pool.png)]

  • 程序当中直接写上的双引号字符串,就在字符串常量池当中
  • 对于基本类型来说 , == 是进行数值的比较
  • 对于引用类型来说 , == 是进行 地址 的比较
  • 两个方法 进行 对字符串内容的比较
    • public boolean equals(Object obj)
    • equals 方法具有对称性(交换律)
    • 如果比较双方是 一个常量一个变量,则推荐把常量字符串写在前面
      • 防止 str为 null 而抛出 NullPointerException
      • 推荐: “abc”.equals(str);
      • 不推荐: str.equals(“abc”); // 防止 str为 null 而抛出 NullPointerException
    • public boolean equalsIgnoreCase(String str)
  • 字符串常量池存在 堆 (Heap) 中

java 基本数据类型对象包装类

定义

为了方便操作基本数据类型值,Java将其封装成了对象,在对象中定义了属性和行为。用于描述该对象的类就称为基本数据类型对象包装类。

基本数据类型对象包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

作用

用于基本数据类型和字符串类型之间做转换。

基本数据类型转成字符串

  • 基本数据类型 + “”
    String num = 1 + "a";
  • 基本数据类型.toString(基本数据类型值);
    Integer.toString(34);// 将34整数变成“34”

字符串转成基本数据类型

  1. 使用包装类中的静态方法:xxx parseXxx(“xxx类型的字符串”)。
    - 只有Character没有parse方法,本身就是封装了字符,无须转换
int a = Integer.parseInt("123");
double b = Double.parseDouble("12.23");
boolean b = Boolean.parseBoolean("true");
  1. 如果字符串被xxx包装类进行对象的封装,使用xxxValue()。
/*格式
Xxx i = new Xxx(String);
xxx num = i.xxxValue();
*/
Integer i = new Integer(String);
int num = i.intValue();

扩展:自动装箱与拆箱

JDK1.5版本以后出现的新特性。

  • **自动装箱:**将一个基本数据类型的值赋给其所对应的包装类类型。
  • **自动拆箱:**将一个基本数据类型包装类类型的值赋给其所对应的基本数据类型。
Integer x = 4;//自动装箱。=new Integer(4)
x = x/* x.intValue() */ + 2;//x+2:x 进行自动拆箱,变成int类型后和2进行加法运算,再将和进行装箱赋给x。

对于新特性,当某一个数值存在于byte范围内时,java不会再开辟新的空间。

class Demo {
		public static void main(String[] args) {

				Integer m = 128;
				Integer n = 128;
				System.out.println("m==n:" + (m == n));//m==n:false
				Integer a = 127;
				Integer b = 127;
				System.out.println("a==b:" + (a == b));//a==b:true。因为a和b指向了同一个Integer对象。
		}
}

集合框架

  • 为什么出现集合类?
    面向对象对事物的体现都是以对象的形式,为了方便对多个对象的操作,就对对象进行存储。
    集合就是存储对象最常用的一种方式.
  • 数组和集合都是容器,两者有何不同?
    • 数组长度固定,而集合长度是可变的
    • 数组值可以存储对象,还可以存储基本数据类型;而集合只能存储对象
      数组存储数据类型是固定的,而集合存储的数据类型不固定
  • 集合类的特点:
    • 集合只能存储对象
    • 集合的长度是可变的
    • 集合可以存储不同类型的对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3s2FKgRV-1596683017894)(C:\Users\windows\Desktop\Project\Markdown\img\java 集合框架.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6lmD3k7C-1596683017896)(C:\Users\windows\Desktop\Project\Markdown\img\java collection.jpeg)]

JDK1.2 版本后,Java完整的提供了类集合的概念。封装了一组强大的、非常方便的集合框架API,让我们在开发中大大提高了效率。

  • 集合中分为三大接口:
    • Collection
    • Map
    • Iterator

集合框架中的接口和类在 java.uitl 包中


  • Collection接口

Collection 层次结构中的根接口。Collection表示一组对象,这些对象也称为collection的元素。一些collection允许有重复的元素,一些不允许。一些是有序的,一些是无序的。JDK不提供此接口的任何直接实现。它提供更具体的子接口(Set、List)实现。此接口通常用来传递collection,并在需要最大普遍性的地方操作这些collection。

  • 接口定义

    	public interface Collection<E> extends Iterable<E>
    
  • List接口
    有序的collection(序列)。此接口的用户可以对列表中每个元素的插入位置进行精确的控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

    • 接口定义

      public interface List<E>extendsCollection<E>
      
    • 特点

      1. 有序的
      2. 允许重复
      3. 允许有多个 null 元素
      4. 具体的实现类:ArrayListVectorLinkedList
    • 如何选择

      1. 安全性 Vector (ArrayList)
      2. 频繁插入、删除操作 LinkedList
      3. 是否遍历 ArrayList
    • ArrayList

      • 实现原理 采用动态数组,默认构造方法创建了一个空数组
      • 第一次添加元素扩充容量为10 ,之后的扩充算法 原 + 原>>1
      • 不适合进行删除操作或插入操作
      • 为了防止数组动态扩充次数太多,建议创建时,给定初始容量。
      • 线程不安全的
      • JDK 1.2 开始
    • Vector

      • 实现原理,采用动态数组实现。默认构造方法创建了一个大小为10 的对象数组
      • 扩充的算法 增量为0时,扩充为 原*2 增量大于0,扩充为 原+增量
      • 不适合进行删除操作或插入操作
      • 为了防止数组动态扩充次数太多,建议创建时,给定初始容量。
      • 线程安全 适合在多线程中使用 在单线程中效率较低
    • LinkedList

      • 实现原理,采用 双向链表结构 实现
      • 适合插入,删除操作 性能高
  • Set接口
    一个不包含重复元素的collection。更确切的讲,set不包含满足e1.equals(e2) 的元素对e1e2,并且最多包含一个 null 元素。正如其名所暗示的,接口模仿了数学上的set抽象。

    • 接口定义

      public interface Set<E> extends Collection<E>
      
    • 特点

      1. 无序的(不保证顺序)
      2. 不允许有重复元素
      3. 具体实现类有:HashSetTreeSetLinkedHashSet
    • 如何选择:

      1. 要排序:选择 TreeSet
      2. 不需要排序也不需要保证插入顺序:选择 HashSet
      3. 不需要排序但需要保证插入顺序:选择 LinkedHashSet
    • HashSet
      类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序。特别是它不保证该顺序恒久不变。允许使用 null 元素

      • 接口定义

        public class HashSet<E> extends AbstractSet<E> implements Set<E>,Cloneable,Serializable、
        
      • 实现原理 基于哈希表(HashMap) 实现 (数组 + 链表)

      • 不允许重复,可以有一个Null元素

      • 不保证顺序恒久不变

      • 添加元素时 把元素作为HashMap的key存储 value使用一个固定的object对象补充。

      • 自定义对象中如果认为属性相同为 相同对象 就需要重写 equals() 和 hashCode()

      • 如何把对象存储到哈希表中: 先计算对象的hashCode值,再对数组的长度取余 来决定存储的数组下标。

      • 解决hashSet中的重复值:判断两个对象是否相同 , 先判断 两个对象的hashCode是否相同。(hashCode相同不一定是同一个对象)如果hashCode不同 那么一定不相同。 如果 hashCode相同,还要进行equals判断,equals相同,则相同,否则不同。

    • TreeSet
      基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序。具体取决于你使用的构造方法

      • 接口定义

        public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>,Cloneable,Serializable
        
      • 有序的

      • 基于TreeMap (二叉树结构)实现 对象需要比较大小 通过比较器实现。

      • 对象比较器还可以去重。

      • 如果自定义的类,没有实现比较器结构将无法add到TreeSet集合中。

    • LinkedHashSet

      具有可预知迭代顺序的Set接口,哈希表和链表实现。此实现与HashSet不同之处在于,LinkedHashset维护着一个运行于所有条目的双重链表。此链表定义了迭代顺序。即按照将元素插入顺序进行迭代。注意插入顺序不受再set中重新插入的元素的影响。(如果再s.contains(e)返回true后立即调用s.add(e), 则元素e会呗重新插入到set 中。

      • 接口定义
        public class LinkedHashSet<E> extends HashSet<E> implements Set<E>,Cloneable , Serializable
        
  • Map 接口
    Map 使用键值对<k,v> 存储数据,map会维护与键k相关联的值v。两个key可以关联相同的对象,但key不能重复,常见的key是String类型,但也可以是任何对象。通过键就可以找到对应的值,这种数据结构就是Map(映射)

    • Map接口中定义的方法:

        void clear() 
        //删除所有的映射
        default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 
        //尝试计算指定键的映射及其当前映射的值(如果没有当前映射, null )。  
        default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 
        /*如果指定的键尚未与值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其输入到此/映射中,除非 null 。 */ 
        default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 
        //如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。  
        boolean containsKey(Object key) 
        //如果此映射包含指定键的映射,则返回 true 。  
        boolean containsValue(Object value) 
        //如果此map将一个或多个键映射到指定的值,则返回 true 。  
        Set<Map.Entry<K,V>> entrySet() 
        //返回此map中包含的映射的Set视图。  
        boolean equals(Object o) 
        //将指定的对象与此映射进行比较以获得相等性。  
        default void forEach(BiConsumer<? super K,? super V> action) 
        //对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。  
        V get(Object key) 
        //返回到指定键所映射的值,或 null  
        default V getOrDefault(Object key, V defaultValue) 
        //返回到指定键所映射的值,或 defaultValue
        int hashCode() 
        //返回此map的哈希码值。  
        boolean isEmpty() 
        //如果此map不包含键值映射,则返回 true 。  
        Set<K> keySet() 
        //返回此map中包含的键的Set视图。  
        default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 
        //如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联。  
        V put(K key, V value) 
        //将指定的值与该映射中的指定键相关联(可选操作)。  
        void putAll(Map<? extends K,? extends V> m) 
        //将指定map的所有映射复制到此映射(可选操作)。  
        default V putIfAbsent(K key, V value) 
        /*如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回null,否则返回当前值。  */
        V remove(Object key) 
        //如果存在(从可选的操作),从该map中删除一个键的映射。  
        default boolean remove(Object key, Object value) 
        //仅当指定的密钥当前映射到指定的值时删除该条目。  
        default V replace(K key, V value) 
        //只有当目标映射到某个值时,才能替换指定键的条目。  
        default boolean replace(K key, V oldValue, V newValue) 
        //仅当当前映射到指定的值时,才能替换指定键的条目。  
        default void replaceAll(BiFunction<? super K,? super V,? extends V> function) 
        //将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。  
        int size() 
        //返回此map中键值映射的数量。  
        Collection<V> values() 
        //返回此map中包含的值的Collection视图。  
      	```
      
      
    • 具体实现类

      • HashMap

        • JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashCode 经过扰动函数(所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法。换句话说使用扰动函数之后可以减少碰撞。)处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

        • JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

        • HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

      • HashTable

        • HashTable和HashMap的实现原理几乎一样,差别无非是
          • 1.HashTable不允许key和value为null;
          • 2.HashTable是线程安全的。
        • 但是HashTable线程安全的策略实现代价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差。Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
      • LinkedHashMap

        • LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。看名字也可以猜到,LinkedHashMap就是在HashMap的基础上加了一个链表来保存记录的插入顺序。
    • TreeMap

      • TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常
  • Iterator 接口
    Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。或者这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。

iterator 定义了三个方法:

返回值方法名
booleanhasNext() 判断容器中是否还有元素
< E >next() 指针下移,返回该指针所指向的元素
voidremove()删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素

使用案例:

Stack<Integer> stack = new Stack<>();
 stack.push(1);
 stack.push(2);
 Iterator<Integer> i = stack.iterator(); 
 while (i.hasNext()) {
     System.out.println(i.next());
  }
List<String> list=new ArrayList<>();
        list.add("abc");
        list.add("edf");
        list.add("ghi");
        for(Iterator<String> it=list.iterator();it.hasNext();)
        {
            System.out.println(it.next());
        }

Collection 集合

  • ArrayList <E> E 是泛型
    * 泛型只能是引用类型,不能是基本类型

System类

 (1)描述系统信息的类
 (2)该类没有构造方法,该类的方法和属性都是静态的
 (3)字段摘要:
	 static InputStream in  “标准”输入流。  
	 static PrintStream out  “标准”输出流。     
 (4)方法摘要:
	 static void exit(int status) 终止当前正在运行的 Java 虚拟机。 
	 static void gc() 运行垃圾回收器。
	 static Properties getProperties()  确定当前的系统属性      
	 static String getProperty(String key) 获取指定键指示的系统属性。   
	 static String getProperty(String key, String def) 获取用指定键描述的系统属性。 
	 static void setIn(InputStream in) 重新分配“标准”输入流。      
	 static void setOut(PrintStream out) 重新分配“标准”输出流。 
	 static void setProperties(Properties props) 将系统属性设置为 Properties 参数。      
	 static String setProperty(String key, String value) 设置指定键指示的系统属性。

Runtime类

 (1)每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
	可以通过 getRuntime 方法获取当前运行时。 应用程序不能创建自己的 Runtime 类实例。
 (2)该类没有构造函数,也就是它不能直接创建对象,但是它里里面的方法又不是静态的
	,故它一定有一个方法返回本类对象
 (3)故该类是单例设计模式,保证在内存中只有一个对象
 (4)方法摘要:
	 Process exec(String command) 在单独的进程中执行指定的字符串命令
	 void gc() 运行垃圾回收器。
	 static Runtime getRuntime() 返回与当前 Java 应用程序相关的运行时对象
	 void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的 Java 虚拟机

JDK1.5新特性

  • 自动装箱与拆箱:

自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。

自动拆箱的过程:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。

自动装箱,只需将该值赋给一个类型包装器引用,java会自动创建一个对象。

自动拆箱,只需将该对象值赋给一个基本类型即可。

java——类的包装器

类型包装器有:Double,Float,Long,Integer,Short,Character和Boolean

  • 枚举

把集合里的对象元素一个一个提取出来。枚举类型使代码更具可读性,理解清晰,易于维护。枚举类型是强类型的,从而保证了系统安全性。而以类的静态字段实现的类似替代模型,不具有枚举的简单性和类型安全性。

简单的用法:JavaEnum简单的用法一般用于代表一组常用常量,可用来代表一类相同类型的常量值。

复杂用法:Java为枚举类型提供了一些内置的方法,同事枚举常量还可以有自己的方法。可以很方便的遍历枚举对象。

  • 静态导入

通过使用 import static,就可以不用指定 Constants 类名而直接使用静态成员,包括静态方法。

import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out。

  • 可变参数(Varargs)

可变参数的简单语法格式为:

methodName([argumentList], dataType… argumentName);

  • 内省(Introspector)

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新 的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter /setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。

一 般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来 调用这些方法。

  • 泛型(Generic)

C++ 通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功能。一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制得类型转换。猛虎引入了泛型,它允许指定集合里元素的类型,这样你可以得到强类型在编译时刻进行类型检查的好处。

  • For-Each循环

For-Each循环得加入简化了集合的遍历。假设我们要遍历一个集合对其中的元素进行一些处理。

JDK1.6新特性

  • Desktop类和SystemTray类

在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray。

前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序.

  • 使用JAXB2来实现对象与XML之间的映射

JAXB是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。

我们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。

实际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。除了JAXB之外,我们还可以通过XMLBeans和Castor等来实现同样的功能。

  • 理解StAX

StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一种处理XML文档的API。

StAX 的来历 :在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).

由 于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX,所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版本). JDK6里面JAXP的版本就是1.4. 。

StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX通过提供一种基于事件迭代器(Iterator)的API让 程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件,然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;

SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM 采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。

  • 使用Compiler API

现在我们可以用JDK6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。

这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去 做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。

  • 轻量级Http Server API

JDK6 提供了一个简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在 这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.

  • 插入式注解处理API(Pluggable Annotation Processing API)

插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)

实 际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.

JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列.

JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,我们想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试方法。

  • 用Console开发控制台程序

JDK6 中提供了java.io.Console 类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但我们不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.

  • 对脚本语言的支持如: ruby, groovy, javascript

  • Common Annotations

Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.

随 着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.

下面列举出Common Annotations 1.0里面的10个Annotations Common Annotations

Annotation Retention Target Description

Generated Source ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用于标注生成的源代码

Resource Runtime TYPE, METHOD, FIELD 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式

Resources Runtime TYPE 同时标注多个外部依赖,容器会把所有这些外部依赖注入

PostConstruct Runtime METHOD 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct

PreDestroy Runtime METHOD 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的

PermitAll Runtime TYPE, METHOD 允许所有角色执行被标注的类或方法

DenyAll Runtime TYPE, METHOD 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行

DeclareRoles Runtime TYPE 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色

注意:

1.RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上

2.标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来

4.处理以上Annotations的工作是由Java EE容器来做, Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做

改动的地方最大的就是java GUI界面的显示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系统的Windows Aero视窗效果,而JDK1.5不支持!!!

你要在vista环境下编程的话最好装jdk6.0,否则它总是换到windows basic视窗效果.

JDK1.7新特性

  • switch中可以使用字串

String s = “test”;
switch (s) {
case “test” :
System.out.println(“test”);
case “test1” :
System.out.println(“test1”);
break ;
default :
System.out.println(“break”);
break ;
}

  • "<>"这个玩意儿的运用List tempList = new ArrayList<>(); 即泛型实例化类型自动推断。
public` `class` `JDK7GenericTest {
	``public` `static` `void` `main(String[] args) {
	 ``// Pre-JDK 7
	 ``List lst1 = ``new` `ArrayList();
	 ``// JDK 7 supports limited type inference for generic instance creation
	 ``List lst2 = ``new` `ArrayList<>();
 
	 ``lst1.add(``"Mon"``);
	 ``lst1.add(``"Tue"``);
	 ``lst2.add(``"Wed"``);
	 ``lst2.add(``"Thu"``);
 
	 ``for` `(String item: lst1) {
		 ``System.out.println(item);
	 ``}
 
	 ``for` `(String item: lst2) {
		 ``System.out.println(item);
	 ``}
	``}
}
  • 自定义自动关闭类

以下是jdk7 api中的接口,(不过注释太长,删掉了close()方法的一部分注释)

/**

 * A resource that must be closed when it is no longer needed.

 *

 * @author Josh Bloch


 * @since 1.7

 */

public interface AutoCloseable {

		/**

		 * Closes this resource, relinquishing any underlying resources.

		 * This method is invoked automatically on objects managed by the

		 * {@code try}-with-resources statement.

		 *

		 */

		void close() throws Exception;

}

只要实现该接口,在该类对象销毁时自动调用close方法,你可以在close方法关闭你想关闭的资源,例子如下

class TryClose implements AutoCloseable {

 

 @Override

 public void close() throw Exception {

	System.out.println(" Custom close method …

																				 close resources ");

 }

}

//请看jdk自带类BufferedReader如何实现close方法(当然还有很多类似类型的类)

	public void close() throws IOException {

				synchronized (lock) {

						if (in == null)

								return;

						in.close();

						in = null;

						cb = null;

				}

		}
  • 新增一些取环境信息的工具方法
File System.getJavaIoTempDir() // IO临时文件夹

File System.getJavaHomeDir() // JRE的安装目录

File System.getUserHomeDir() // 当前用户目录

File System.getUserDir() // 启动java进程时所在的目录

.......
  • Boolean类型反转,空指针安全,参与位运算
Boolean Booleans.negate(Boolean booleanObj)

True => False , False => True, Null => Null

boolean Booleans.and(boolean[] array)

boolean Booleans.or(boolean[] array)

boolean Booleans.xor(boolean[] array)

boolean Booleans.and(Boolean[] array)

boolean Booleans.or(Boolean[] array)

boolean Booleans.xor(Boolean[] array)
  • 两个char间的equals
boolean Character.equalsIgnoreCase(char ch1, char ch2)
  • 安全的加减乘除
int Math.safeToInt(long value)

int Math.safeNegate(int value)

long Math.safeSubtract(long value1, int value2)

long Math.safeSubtract(long value1, long value2)

int Math.safeMultiply(int value1, int value2)

long Math.safeMultiply(long value1, int value2)

long Math.safeMultiply(long value1, long value2)

long Math.safeNegate(long value)

int Math.safeAdd(int value1, int value2)

long Math.safeAdd(long value1, int value2)

long Math.safeAdd(long value1, long value2)

int Math.safeSubtract(int value1, int value2)
  • 对Java集合(Collections)的增强支持

在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下:

以List、Set、Map集合容器为例:

//创建List接口对象


		List<String> list=new ArrayList<String>();

		list.add("item"); //用add()方法获取对象

		String Item=list.get(0); //用get()方法获取对象

		//创建Set接口对象

		Set<String> set=new HashSet<String>();

		set.add("item"); //用add()方法添加对象

		//创建Map接口对象

		Map<String,Integer> map=new HashMap<String,Integer>();

		map.put("key",1); //用put()方法添加对象

		int value=map.get("key");

在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下:

List<String> list=["item"]; //向List集合中添加元素

			String item=list[0]; //从List集合中获取元素

			Set<String> set={"item"}; //向Set集合对象中添加元素

			Map<String,Integer> map={"key":1}; //向Map集合中添加对象

			int value=map["key"]; //从Map集合中获取对象
  • 数值可加下划线
例如:int one_million = 1_000_000;
  • 支持二进制文字
例如:int binary = 0b1001_1001;
  • 简化了可变参数方法的调用

当程序员试图使用一个不可具体化的可变参数并调用一个varargs (可变)方法时,编辑器会生成一个“非安全操作”的警告。

  • 在try catch异常扑捉中,一个catch可以写多个异常类型,用"|"隔开

jdk7之前:

try {
	 ......
} catch(ClassNotFoundException ex) {
	 ex.printStackTrace();
} catch(SQLException ex) {
	 ex.printStackTrace();
}

jdk7例子如下

try {
	 ......
} catch(ClassNotFoundException|SQLException ex) {
	 ex.printStackTrace();
}
  • jdk7之前,你必须用try{}finally{}在try内使用资源,在finally中关闭资源,不管try中的代码是否正常退出或者异常退出。jdk7之后,你可以不必要写finally语句来关闭资源,只要你在try()的括号内部定义要使用的资源

jdk7之前:

import java.io.*;
// Copy from one file to another file character by character.
// Pre-JDK 7 requires you to close the resources using a finally block.
public class FileCopyPreJDK7 {
	 public static void main(String[] args) {
			BufferedReader in = null;
			BufferedWriter out = null;
			try {
				 in  = new BufferedReader(new FileReader("in.txt"));
				 out = new BufferedWriter(new FileWriter("out.txt"));
				 int charRead;
				 while ((charRead = in.read()) != -1) {
						System.out.printf("%c ", (char)charRead);
						out.write(charRead);
				 }
			} catch (IOException ex) {
				 ex.printStackTrace();
			} finally {            // always close the streams
				 try {
						if (in != null) in.close();
						if (out != null) out.close();
				 } catch (IOException ex) {
						ex.printStackTrace();
				 }
			}
 
			try {
				 in.read();   // Trigger IOException: Stream closed
			} catch (IOException ex) {
				 ex.printStackTrace();
			}
	 }
}

jdk7之后

import java.io.*;
// Copy from one file to another file character by character.
// JDK 7 has a try-with-resources statement, which ensures that
// each resource opened in try() is closed at the end of the statement.
public class FileCopyJDK7 {
	 public static void main(String[] args) {
			try (BufferedReader in  = new BufferedReader(new FileReader("in.txt"));
					 BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"))) {
				 int charRead;
				 while ((charRead = in.read()) != -1) {
						System.out.printf("%c ", (char)charRead);
						out.write(charRead);
				 }
			} catch (IOException ex) {
				 ex.printStackTrace();
			}
	 }
}

JDK1.8新特性

  • default关键字

在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现,如下。

public interface NewCharacter {
		
		public void test1();
		
		public default void test2(){
				System.out.println("我是新特性1");
		}

}

那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?

其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。

public class NewCharacterImpl implements NewCharacter{

		@Override
		public void test1() {
				
		}
		
		public static void main(String[] args) {
				NewCharacter nca = new NewCharacterImpl();
				nca.test2();
		}

}

所以说这个default方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk的集合List里面增加了一个sort方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList等都需要对其添加实现,那么现在用default定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

  • Lambda 表达式

Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。

首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

简单的来说就是,函数也是一等公民了,在java里面一等公民有变量,对象,那么函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,看一下下面这个例子。

//这是常规的Collections的排序的写法,需要对接口方法重写
				public void test1(){
				List<String> list =Arrays.asList("aaa","fsa","ser","eere");
				Collections.sort(list, new Comparator<String>() {
						@Override
						public int compare(String o1, String o2) {
								return o2.compareTo(o1);
						}
				});
				for (String string : list) {
						System.out.println(string);
				}
		}
//这是带参数类型的Lambda的写法
				public void testLamda1(){
				List<String> list =Arrays.asList("aaa","fsa","ser","eere");
				Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
						return b.compareTo(a);
				}
				);
				for (String string : list) {
						System.out.println(string);
				}
		}
//这是不带参数的lambda的写法
				public void testLamda2(){
				List<String> list =Arrays.asList("aaa","fsa","ser","eere");
				Collections.sort(list, (a,b)->b.compareTo(a)
				);
				for (String string : list) {
						System.out.println(string);
				}

可以看到不带参数的写法一句话就搞定了排序的问题,所以引入lambda表达式的一个最直观的作用就是大大的简化了代码的开发,像其他一些编程语言Scala,Python等都是支持函数式的写法的。当然,不是所有的接口都可以通过这种方法来调用,只有函数式接口才行,jdk1.8里面定义了好多个函数式接口,我们也可以自己定义一个来调用,下面说一下什么是函数式接口。

  • 函数式接口

定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。

@FunctionalInterface
public interface MyLamda {
		
		public void test1(String y);

//这里如果继续加一个抽象方法便会报错
//    public void test1();
		
//default方法可以任意定义
		default String test2(){
				return "123";
		}
		
		default String test3(){
				return "123";
		}

//static方法也可以定义
		static void test4(){
				System.out.println("234");
		}

}

看一下这个接口的调用,符合lambda表达式的调用方法。

MyLamda m = y -> System.out.println("ss"+y);
  • 方法与构造函数引用

jdk1.8提供了另外一种调用方式::,当 你 需 要使用 方 法 引用时 , 目 标引用 放 在 分隔符::前 ,方法 的 名 称放在 后 面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法,如下示例。

//先定义一个函数式接口
@FunctionalInterface
public interface TestConverT<T, F> {
		F convert(T t);
}

测试如下,可以以::形式调用。

public void test(){
		TestConverT<String, Integer> t = Integer::valueOf;
		Integer i = t.convert("111");
		System.out.println(i);
}

此外,对于构造方法也可以这么调用。

//实体类User和它的构造方法
public class User {    
		private String name;
		
		private String sex;

		public User(String name, String sex) {
				super();
				this.name = name;
				this.sex = sex;
		}
}
//User工厂
public interface UserFactory {
		User get(String name, String sex);
}
//测试类
		UserFactory uf = User::new;
		User u = uf.get("ww", "man");

这里的User::new就是调用了User的构造方法,Java编译器会自动根据UserFactory.get方法的签名来选择合适的构造函数。

  • 局部变量限制

Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda。 Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final,或事实上是final。
  为什么局部变量有这些限制?
  (1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
  (2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。

final int num = 1;
Converter<Integer, String> stringConverter =
				(from) -> String.valueOf(from + num);
stringConverter.convert(2); 
  • Date Api更新

1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。

6.1 LocalDate/LocalTime/LocalDateTime

LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。

now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plus或minus方法可以增减日期或时间信息;

6.2TemporalAdjusters

这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。

6.3DateTimeFormatter

以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:

LocalDateTime dt = LocalDateTime.now();  
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");         
System.out.println(dtf.format(dt));

定义:流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

流的操作类型分为两种:

  • Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

  • Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

      在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
    

构造流的几种方式

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
  • Objects方法新特性
//比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);

//深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的对应下标值是否相同)
public static boolean deepEquals(Object a, Object b);

//返回对象的hashCode,若传入的为null,返回0
public static int hashCode(Object o);

//返回传入可变参数的所有值的hashCode的总和(这里说总和有点牵强,具体参考Arrays.hashCode()方法)
public static int hash(Object... values);

//返回对象的String表示,若传入null,返回null字符串
public static String toString(Object o)

//返回对象的String表示,若传入null,返回默认值nullDefault
public static String toString(Object o, String nullDefault)

//使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
public static <T> int compare(T a, T b, Comparator<? super T> c) 

//如果传入的obj为null抛出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj) 

//如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)

-----------------------------以下是jdk8新增方法---------------------------

//判断传入的obj是否为null,是返回true,否者返回false
public static boolean isNull(Object obj)

//判断传入的obj是否不为null,不为空返回true,为空返回false (和isNull()方法相反)
public static boolean nonNull(Object obj)

//如果传入的obj为null抛出NullPointerException并且使用参数messageSupplier指定错误信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)

下面是几个简单的实例:

package cn.cupcat.java8;

import org.junit.Test;

import java.util.Comparator;
import java.util.Objects;

/**
 * Created by xy on 2017/12/25.
 */
public class ObjectsTest {

		/**
		 *  因为Objects类比较简单,所以只用这一个测试用例进行测试
		 * */
		@Test
		public void equalsTest(){
				String str1 = "hello";
				String str2 = "hello";
				//传入对象
				//Objects.equals(str1, str2) ?  true
				boolean equals = Objects.equals(str1, str2);
				System.out.println("Objects.equals(str1, str2) ?  "+ equals);



		}
		@Test
		public void deepEqualsTest(){
				String str1 = "hello";
				String str2 = "hello";
				//传入对象
				boolean deepEquals = Objects.deepEquals(str1, str2);
				//Objects.deepEquals(str1, str2) ?  true
				System.out.println("Objects.deepEquals(str1, str2) ?  "+ deepEquals);
				int[] arr1 = {1,2};
				int[] arr2 = {1,2};
				//传入数组
				deepEquals = Objects.deepEquals(arr1, arr2);
				//Objects.deepEquals(arr1, arr2) ?  true
				System.out.println("Objects.deepEquals(arr1, arr2) ?  "+ deepEquals);
		}
		@Test
		public void hashCodeTest(){
				String str1 = "hello";

				//传入对象
				int hashCode = Objects.hashCode(str1);
				//Objects.hashCode(str1) ?  99162322
				System.out.println("Objects.hashCode(str1) ?  "+ hashCode);

				//传入null
				hashCode = Objects.hashCode(null);
				//Objects.hashCode(null) ?  0
				System.out.println("Objects.hashCode(null) ?  "+ hashCode);


		}

		@Test
		public void hashTest(){

				int  a  = 100;

				//传入对象
				int hashCode = Objects.hashCode(a);
				//Objects.hashCode(str1) ?  100
				System.out.println("Objects.hashCode(str1) ?  "+ hashCode);

				//输入数组
				int[] arr = {100,100};
				hashCode = Objects.hash(arr);
				//Objects.hashCode(arr) ?  1555093793
				System.out.println("Objects.hashCode(arr) ?  "+ hashCode);
		}


		@Test
		public void compareTest(){
				int a = 10;
				int b = 11;
				int compare = Objects.compare(a, b, new Comparator<Integer>() {
						@Override
						public int compare(Integer o1, Integer o2) {
								return o1.compareTo(o2);
						}
				});
				// compare = -1
				System.out.println(" compare = "+ compare);

		}

		@Test
		public void requireNonNullTest(){
				String test = null;
				//java.lang.NullPointerException
			 // String s = Objects.requireNonNull(test);

				//java.lang.NullPointerException: 这是空指针异常提示的信息
				//String s = Objects.requireNonNull(test, "这是空指针异常提示的信息");

				
				//java.lang.NullPointerException: 我是返回的异常信息
				String s = Objects.requireNonNull(test,()->"我是返回的异常信息");
		}
}

java io 流

  • 基本概念

IO:Java对数据的操作是通过流的方式,IO流用来处理设备之间的数据传输,上传文件和下载文件,Java用于操作流的对象都在IO包中。

  • IO流的分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQYUUANY-1596683017897)(C:\Users\windows\Desktop\Project\Markdown\img\java io.png)]

  • 字节流

  • 字节流基类

  • InputStream

InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类。

 常用方法:

		// 从输入流中读取数据的下一个字节
		abstract int read()
		// 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中
		int read(byte[] b)
		// 将输入流中最多 len 个数据字节读入 byte 数组
		int read(byte[] b, int off, int len)


		// 跳过和丢弃此输入流中数据的 n个字节
		long skip(long n)

		// 关闭此输入流并释放与该流关联的所有系统资源
		void close()
  • OutputStream

OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。

 常用方法:
		// 将 b.length 个字节从指定的 byte 数组写入此输出流
		void write(byte[] b)
		// 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
		void write(byte[] b, int off, int len)
		// 将指定的字节写入此输出流
		abstract void write(int b)

		// 关闭此输出流并释放与此流有关的所有系统资源
		void close()

		// 刷新此输出流并强制写出所有缓冲的输出字节
		void flush()
  • 字节文件操作流

  • FileInputStream

FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。

 构造方法:
		// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
		FileInputStream(File file)
		// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定
		FileInputStream(String name)

 常用方法:覆盖和重写了父类的的常用方法。
				// 读取f盘下该文件f://hell/test.txt
				//构造方法1
				InputStream inputStream = new FileInputStream(new File("f://hello//test.txt"));
				int i = 0;
				//一次读取一个字节
				while ((i = inputStream.read()) != -1) {

						// System.out.print(i + " ");// 65 66 67 68
						//为什么会输出65 66 67 68?因为字符在底层存储的时候就是存储的数值。即字符对应的ASCII码。
						System.out.print((char) i + " ");// A B C D
				}
				//关闭IO流
				inputStream.close();
				// 读取f盘下该文件f://hell/test.txt
				//构造方法2
				InputStream inputStream2 = new FileInputStream("f://hello/test.txt");
				// 字节数组
				byte[] b = new byte[2];
				int i2 = 0;
				//  一次读取一个字节数组
				while ((i2 = inputStream2.read(b)) != -1) {

						System.out.print(new String(b, 0, i2) + " ");// AB CD
				}
				//关闭IO流
				inputStream2.close();

注: 一次读取一个字节数组,提高了操作效率,IO流使用完毕一定要关闭。

  • FileOutputStream

FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。

 构造方法:
		// 创建一个向指定File对象表示的文件中写入数据的文件输出流
		FileOutputStream(File file)
		// 创建一个向指定File对象表示的文件中写入数据的文件输出流
		FileOutputStream(File file, boolean append)
		// 创建一个向具有指定名称的文件中写入数据的输出文件流
		FileOutputStream(String name)
		// 创建一个向具有指定name的文件中写入数据的输出文件流
		FileOutputStream(String name, boolean append)

 常用方法:覆盖和重写了父类的的常用方法。
				OutputStream outputStream = new FileOutputStream(new File("test.txt"));
				// 写出数据
				outputStream.write("ABCD".getBytes());
				// 关闭IO流
				outputStream.close();

				// 内容追加写入
				OutputStream outputStream2 = new FileOutputStream("test.txt", true);
				// 输出换行符
				outputStream2.write("\r\n".getBytes());
				// 输出追加内容
				outputStream2.write("hello".getBytes());
				// 关闭IO流
				outputStream2.close();

注;输出的目的地文件不存在,则会自动创建,不指定盘符的话,默认创建在项目目录下;输出换行符时一定要写\r\n不能只写\n,因为不同文本编辑器对换行符的识别存在差异性。

  • 字节缓冲流(高效流)

  • BufferedInputStream

BufferedInputStream:字节缓冲输入流,提高了读取效率。

		 构造方法:
		 // 创建一个 BufferedInputStream并保存其参数,即输入流in,以便将来使用。
		 BufferedInputStream(InputStream in)
		 // 创建具有指定缓冲区大小的 BufferedInputStream并保存其参数,即输入流in以便将来使用
		 BufferedInputStream(InputStream in, int size)
				InputStream in = new FileInputStream("test.txt");
				// 字节缓存流
				BufferedInputStream bis = new BufferedInputStream(in);
				byte[] bs = new byte[20];
				int len = 0;
				while ((len = bis.read(bs)) != -1) {

						System.out.print(new String(bs, 0, len));
						// ABCD
						// hello
				}
				// 关闭流
				bis.close();
  • BufferedOutputStream

BufferedOutputStream:字节缓冲输出流,提高了写出效率。

		 构造方法:
		 // 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
		 BufferedOutputStream(OutputStream out)
		 // 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
		 BufferedOutputStream(OutputStream out, int size)

		 常用方法:
		 // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流
		 void write(byte[] b, int off, int len)
		 // 将指定的字节写入此缓冲的输出流
		 void write(int b)
		 // 刷新此缓冲的输出流
		 void flush()
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt", true));
				// 输出换行符
				bos.write("\r\n".getBytes());
				// 输出内容
				bos.write("Hello Android".getBytes());
				// 刷新此缓冲的输出流
				bos.flush();
				// 关闭流
				bos.close();
  • 字符流

  • 字符流基类

  • Reader

Reader:读取字符流的抽象类.

	常用方法:
		// 读取单个字符
		int read()
		// 将字符读入数组
		int read(char[] cbuf)
		// 将字符读入数组的某一部分
		abstract int read(char[] cbuf, int off, int len)
		// 跳过字符
		long skip(long n)

		// 关闭该流并释放与之关联的所有资源
		abstract void close()
  • Writer

Writer:写入字符流的抽象类.

 常用方法:
		// 写入字符数组
		 void write(char[] cbuf)
		// 写入字符数组的某一部分
		abstract void write(char[] cbuf, int off, int len)
		// 写入单个字符
		void write(int c)
		// 写入字符串
		void write(String str)
		// 写入字符串的某一部分
		void write(String str, int off, int len)

		// 将指定字符添加到此 writer
		Writer append(char c)
		// 将指定字符序列添加到此 writer
		Writer append(CharSequence csq)
		// 将指定字符序列的子序列添加到此 writer.Appendable
		Writer append(CharSequence csq, int start, int end)

		// 关闭此流,但要先刷新它
		abstract void close()
		// 刷新该流的缓冲
		abstract void flush()
  • 字符转换流

  • InputStreamReader

InputStreamReader:字节流转字符流,它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

 构造方法:
		// 创建一个使用默认字符集的 InputStreamReader
		InputStreamReader(InputStream in)
		// 创建使用给定字符集的 InputStreamReader
		InputStreamReader(InputStream in, Charset cs)
		// 创建使用给定字符集解码器的 InputStreamReader
		InputStreamReader(InputStream in, CharsetDecoder dec)
		// 创建使用指定字符集的 InputStreamReader
		InputStreamReader(InputStream in, String charsetName)
 特有方法:
		//返回此流使用的字符编码的名称 
		String getEncoding() 
				//使用默认编码        
				InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"));
				int len;
				while ((len = reader.read()) != -1) {
						System.out.print((char) len);//爱生活,爱Android

				}
				reader.close();

				 //指定编码 
				InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"),"utf-8");
				int len;
				while ((len = reader.read()) != -1) {
						System.out.print((char) len);//????????Android
				}
				reader.close();

注:Eclipse默认使用GBK编码,test.txt文件所以是GBK编码,当指定utf-8编码时所以会乱码。

  • OutputStreamWriter

OutputStreamWriter:字节流转字符流。

 构造方法:
		// 创建使用默认字符编码的 OutputStreamWriter
		OutputStreamWriter(OutputStream out)
		// 创建使用给定字符集的 OutputStreamWriter
		OutputStreamWriter(OutputStream out, Charset cs)
		// 创建使用给定字符集编码器的 OutputStreamWriter
		OutputStreamWriter(OutputStream out, CharsetEncoder enc)
		// 创建使用指定字符集的 OutputStreamWriter
		OutputStreamWriter(OutputStream out, String charsetName)
 特有方法:
		//返回此流使用的字符编码的名称 
		String getEncoding() 
  • 字符缓冲流(高效流)

  • BufferedReader

BufferedReader:字符缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

 构造方法:
		// 创建一个使用默认大小输入缓冲区的缓冲字符输入流
		BufferedReader(Reader in)
		// 创建一个使用指定大小输入缓冲区的缓冲字符输入流
		BufferedReader(Reader in, int sz)
 特有方法:
		// 读取一个文本行
		String readLine()
				//生成字符缓冲流对象
				BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt")));
				String str;
				//一次性读取一行
				while ((str = reader.readLine()) != null) {
						System.out.println(str);// 爱生活,爱Android
				}

				//关闭流
				reader.close();
  • BufferedWriter

BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

 构造方法:
		// 创建一个使用默认大小输出缓冲区的缓冲字符输出流
		BufferedWriter(Writer out)
		// 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
		BufferedWriter(Writer out, int sz)
 特有方法:
		// 写入一个行分隔符
		void newLine() 
  • FileReader、FileWriter
 FileReader:InputStreamReader类的直接子类,用来读取字符文件的便捷类,使用默认字符编码。
 FileWriter:OutputStreamWriter类的直接子类,用来写入字符文件的便捷类,使用默认字符编码。
  • 高效流效率比对
读取f盘下的一个视频文件到项目中:文件大小29.5 MB
  • 读取方式一:
				FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
				FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
				int len;
				// 开始时间
				long begin = System.currentTimeMillis();
				// 一次读取一个字节
				while ((len = inputStream.read()) != -1) {
						outputStream.write(len);
				}
				// 用时毫秒
				System.out.println(System.currentTimeMillis() - begin);// 213195
				//关闭流释放资源
				inputStream.close();
				outputStream.close();
  • 读取方式二:
				FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
				FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
				int len;
				byte[] bs = new byte[1024];
				// 开始时间
				long begin = System.currentTimeMillis();
				// 一次读取一个字节数组
				while ((len = inputStream.read(bs)) != -1) {
						outputStream.write(bs, 0, len);
				}
				// 用时毫秒
				System.out.println(System.currentTimeMillis() - begin);// 281

				inputStream.close();
				outputStream.close();
  • 读取方式三:
				FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
				BufferedInputStream bis = new BufferedInputStream(inputStream);
				FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
				BufferedOutputStream bos = new BufferedOutputStream(outputStream);
				int len;
				byte[] bs = new byte[1024];
				// 开始时间
				long begin = System.currentTimeMillis();
				while ((len = bis.read(bs)) != -1) {
						bos.write(bs, 0, len);
				}
				// 用时毫秒
				System.out.println(System.currentTimeMillis() - begin);// 78

				bis.close();
				bos.close();

注:由此可以看出高效缓冲流读写速度是非常快的,建议使用。

数组初始化

数组一旦创建 ,其长度不可改变

  • 动态初始化(指定长度)

    • 数据类型 [] 数组名 = new 数据类型 [数组长度]
  • 静态初始化(指定内容)创建时不指定元素个数,而是直接指定具体数据内容,数组长度自动推算

    • 数据类型 [] 数组名 = new 数据类型 [] {元素 ,元素2 , …}
    • 或 数据类型 [] 数组名 = {元素 ,元素2 , …}

Arrays类

  • java.util.Arrays 是一个与数组有关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作
  • 方法
    • toString 将参数数组变成 字符串
    • sort 默认按升序(从小到大)对数组进行排序

java继承中 成员(重名)变量访问特点

  • 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式
    • 直接通过子类对象访问成员变量
      • 等号左边是谁,就优先用谁,没有则向上找。
    • 间接通过成员方法访问成员变量
      • 该方法属于谁,就优先用谁,没有则向上找。
  • 区分子类方法中的三种重名的变量。
    • 本类局部变量: 直接写变量名
    • 本类当中的成员变量:this.成员变量名
    • 父类当中的成员变量:super.成员变量名

java继承中 成员(重名)方法访问特点

  • 在父子类的继承关系当中,创建子类对象,访问成员方法的规则
    • 创建的对象是谁,就优先用谁,如果没有则向上找。
  • 注意事项:
    • 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。

java方法重写的注意事项

  • 父类中私有方法不能被重写。因为父类的私有方法子类根本就无法继承。
  • 子类重写父类方法时,访问权限不能更低。最好就一致。
  • 父类静态方法,子类也必须通过静态方法进行重写。其实这个算不上方法重写,但是现象确实如此。(静态只能覆盖静态)
  • 子类重写父类方法的时候,最好声明一模一样。
  • 在 java 1.4版本以前,重写方法的返回值类型被要求必须与被重写方法一致,但是在java 5.0中放宽了这一个限制,添加了对协变返回类型的支持,在重写的时候,重写方法的返回值类型可以是被重写方法返回值类型的子类。(即子类方法的返回值必须小于或等于父类方法的返回值范围)

java抽象类注意事项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRd0FIEi-1596683017898)(C:\Users\windows\Desktop\Project\Markdown\img\java abstract class notice.png)]

java 多态 向上转型 向下转型

类似 C++ 的 多态 基类指针 虚函数

java权限修饰符

修饰符类内部同一个包子类任何地方
privateYES
(default)YESYES
protectedYESYESYES
publicYESYESYESYES

java 多线程 (重中之重)

  • 主线程:执行 main() 方法的线程

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

  • 并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事

  • 线程的相关API

    • Thread.currentThread().getName()//获取当前线程的名字
    • start():1.启动当前线程2.调用线程中的run方法
    • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    • currentThread():静态方法,返回执行当前代码的线程
    • getName():获取当前线程的名字
    • setName():设置当前线程的名字
    • yield():主动释放当前线程的执行权
    • join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去
    • stop():过时方法。当执行此方法时,强制结束当前线程。
    • sleep(long millitime):线程休眠一段时间
    • isAlive():判断当前线程是否存活
  • 线程的调度

    • 调度策略
      • 时间片:线程的调度采用时间片轮转的方式
      • 抢占式:高优先级的线程抢占CPU
    • Java的调度方法
      • 对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略
      • 对高优先级,使用优先调度的抢占式策略
  • 线程的优先级

    • 等级
      • MAX_PRIORITY:10
      • MIN_PRIORITY:1
      • NORM_PRIORITY:5
    • 方法:
      • getPriority():返回线程优先级
      • setPriority(int newPriority):改变线程的优先级
    • 注意!:高优先级的线程要抢占低优先级的线程的cpu的执行权。但是仅是从概率上来说的,高优先级的线程更有可能被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。
  • 多线程的创建方式

    • 继承Thread类

      1. 创建一个集成于Thread类的子类
      2. 重写Thread类的run()方法
      3. 创建Thread子类的对象
      4. 通过此对象调用start()方法
    • 实现Runable接口

      1. 创建一个实现了Runable接口的类
      2. 实现类去实现Runnable中的抽象方法:run()
      3. 创建实现类的对象
      4. 将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
      5. 通过Thread类的对象调用start()
    • 实现callable接口

      1. 创建一个实现callable的实现类
      2. 实现call方法
      3. 创建callable实现类的对象
      4. 将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象
      5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值)
      • 与使用runnable方式相比,callable功能更强大些:
      • runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值
      • 方法可以抛出异常
      • 支持泛型的返回值
      • 需要借助FutureTask类,比如获取返回结果
    • 使用线程池

      • 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

      • 思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。(数据库连接池)

      • 好处:提高响应速度(减少了创建新线程的时间)。降低资源消耗(重复利用线程池中线程,不需要每次都创建)。便于线程管理。

      • 创建线程池的7个参数

        1. corePoolSize线程池的核心线程数
        2. maximumPoolSize能容纳的最大线程数
        3. keepAliveTime空闲线程存活时间
        4. unit 存活的时间单位
        5. workQueue 存放提交但未执行任务的队列
        6. threadFactory 创建线程的工厂类
        7. handler 等待队列满后的拒绝策略
      • JDK 5.0 起提供了线程池相关API:

        Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
        Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池
        Executors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池
        Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
        Executors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
        • ExecutorService 和 Executors
        • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
        • void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
        • Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable
        • void shutdown():关闭连接池。
      1. 创建实现runnable或者callable接口方式的对象
      2. 创建 executorservice 线程池
      3. 将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
      4. 关闭线程池
  • 线程通信方法

    • wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。
    • wait()/ notify()/ notifayAll() 这三个方法均只能使用在同步代码块或者同步方法中。
    1. 每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的等待队列是空的。
    2. 我们应该在某个线程获取对象的锁后,在该对象锁控制的同步块或同步方法中去调用该对象的wait方法,将该线程挂起放入该对象等待队列。
    3. 当调用该对象的notify方法时,将从该对象的等待队列中随机唤醒一个线程,这个线程将再次成为可运行的线程。
    4. 所以我们使用wait和notify方法的时候必须确定这两个线程在同一个对象(必须是控制该同步块或同步方法的对象)的等待队列。
  • 线程的分类

    • 守护线程(如垃圾回收线程,异常处理线程),
    • 用户线程(如主线程)
  • 线程的生命周期
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMgayY9P-1596683017900)(C:\Users\windows\Desktop\Project\Markdown\img\java 线程 生命周期.png)]

  • 解决线程的安全问题

    1. 同步代码块 ,使用同步监视器(锁)(任何一个的对象都可以充当锁)。
         Synchronized(同步监视器){
         //需要被同步的代码
         }
      
     * Runable天生共享锁,而Thread中需要用static对象或者this关键字或者当前类来充当唯一锁
    2. 同步方法,对方法进行synchronized关键字修饰
    3. JDK5.0新增的lock锁方法 (ReentrantLock )
    
    
  • sleep和wait的异同

    • 相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
    • 不同点:
      1. 两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
      2. 调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
      3. 关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放

lambda 表达式 (重点)

函数式接口 ???

Stream (重点)

方法引用

Junit

反射

注解

小知识点

javac 编译器自动类型强制转换

  • 对于 byte/short/char 三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器将会自动隐式地补上一个 (byte)(short)(char)
    • 如果没有超过类型对应的范围,则编译器自动补上强制类型转换
    • 如果超过类型对应的范围,则编译时报错
  • byte/short/char这三种类型在运算的时候,都会被首先提升成为int类型,然后再计算

javac 编译器编译时计算常量(编译器的常量优化)

在给变量进行赋值时,如果右侧的表达式当中全部是常量,则编译器将在编译时计算表达式

java 不可变

  • 对于基本类型,不可变指的是变量的数值不可变

  • 对于引用类型,不可变指的是 变量的 地址值 不可变
    队列
    6. threadFactory 创建线程的工厂类
    7. handler 等待队列满后的拒绝策略

    • JDK 5.0 起提供了线程池相关API:

      Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
      Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池
      Executors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池
      Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
      Executors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
      • ExecutorService 和 Executors
      • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
      • void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
      • Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable
      • void shutdown():关闭连接池。
    1. 创建实现runnable或者callable接口方式的对象
    2. 创建 executorservice 线程池
    3. 将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
    4. 关闭线程池
  • 线程通信方法

    • wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。
    • wait()/ notify()/ notifayAll() 这三个方法均只能使用在同步代码块或者同步方法中。
    1. 每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的等待队列是空的。
    2. 我们应该在某个线程获取对象的锁后,在该对象锁控制的同步块或同步方法中去调用该对象的wait方法,将该线程挂起放入该对象等待队列。
    3. 当调用该对象的notify方法时,将从该对象的等待队列中随机唤醒一个线程,这个线程将再次成为可运行的线程。
    4. 所以我们使用wait和notify方法的时候必须确定这两个线程在同一个对象(必须是控制该同步块或同步方法的对象)的等待队列。
  • 线程的分类

    • 守护线程(如垃圾回收线程,异常处理线程),
    • 用户线程(如主线程)
  • 线程的生命周期
    [外链图片转存中…(img-sMgayY9P-1596683017900)]

  • 解决线程的安全问题

    1. 同步代码块 ,使用同步监视器(锁)(任何一个的对象都可以充当锁)。
         Synchronized(同步监视器){
         //需要被同步的代码
         }
      
     * Runable天生共享锁,而Thread中需要用static对象或者this关键字或者当前类来充当唯一锁
    2. 同步方法,对方法进行synchronized关键字修饰
    3. JDK5.0新增的lock锁方法 (ReentrantLock )
    
    
  • sleep和wait的异同

    • 相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
    • 不同点:
      1. 两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
      2. 调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
      3. 关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放

lambda 表达式 (重点)

函数式接口 ???

Stream (重点)

方法引用

Junit

反射

注解

小知识点

javac 编译器自动类型强制转换

  • 对于 byte/short/char 三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器将会自动隐式地补上一个 (byte)(short)(char)
    • 如果没有超过类型对应的范围,则编译器自动补上强制类型转换
    • 如果超过类型对应的范围,则编译时报错
  • byte/short/char这三种类型在运算的时候,都会被首先提升成为int类型,然后再计算

javac 编译器编译时计算常量(编译器的常量优化)

在给变量进行赋值时,如果右侧的表达式当中全部是常量,则编译器将在编译时计算表达式

java 不可变

  • 对于基本类型,不可变指的是变量的数值不可变
  • 对于引用类型,不可变指的是 变量的 地址值 不可变
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值