Java是什么?我就是死都不会学Java

1.注释
注释的重要性不言而喻,我们不管写什么代码注释必不可少,那么java的注释的书写方式与注释模板是怎么进行的呢?我们来看一下。
package frist;
class HelloWorld {
public static void main(String[] arg)
{
System.out.println("wyl");
}
}
1.1 注释
以上可以看出java的注释主要有三种 单行注释:只能注释当前行,以//开始,直到行结束
多行注释:注释一段文字,以/*开始, */结束!
文档注释:用于生产API文档,配合JavaDoc。
1.2 idea注释模版配置
1.2.1 定义java文件头部的注释
File => setting => editor => File and Code Templates-class -Includes 
以上当你创建一个class的时候就会带上以上信息了
1.2.2 给java类中的方法添加上注释
第一步勾选Enable Live Templates 首先要在上一步中勾选中 Enable Live Templates 
第二步新建一个Group 其次要打开LiveTemplates 然后新建一个Group 如图: 
在弹窗口中输入你想要的group名称,wyl 
其中:Abbreviation 为快捷键,当输入w的时候就会提示对应的方法注释模板,j为类的注释模板 
Templete Text 注释内容,$$ 为动态模板参数点击Edit Vaariables 选择对应动态值。

public class wyl {
public String CommentTemplate(String str)
{
return str;
}
}
2.关键字
关键字 | 说明 |
---|
private | 一种访问控制方式:私用模式 | protected | 一种访问控制方式:保护模式 | public | 一种访问控制方式:共用模式 | abstract | 表明类或者成员方法具有抽象属性 | class | 类 | extends | 表明一个类型是另一个类型的子类型,这里常见的类型有类和接口 | final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变 | implements | 表明一个类实现了给定的接口 | interface | 接口 | native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 | new | 用来创建新实例对象 | static | 表明具有静态属性 | strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 | synchronized | 表明一段代码需要同步执行 | transient | 声明不用序列化的成员域 | volatile | 表明两个或者多个变量必须同步地发生变化 | break | 提前跳出一个块 | continue | 回到一个块的开始处 | return | 从成员方法中返回数据 | do | 用在do-while循环结构中 | while | 用在循环结构中 | if | 条件语句的引导词 | else | 用在条件语句中,表明当条件不成立时的分支 | for | 一种循环结构的引导词 | instanceof | 用来测试一个对象是否是指定类型的实例对象 | switch | 分支语句结构的引导词 | case | 用在switch语句之中,表示其中的一个分支 | default | 默认,例如,用在switch语句中,表明一个默认的分支 | try | 尝试一个可能抛出异常的程序块 | catch | 用在异常处理中,用来捕捉异常 | throw | 抛出一个异常 | throws | 声明在当前定义的成员方法中所有需要抛出的异常 | import | 表明要访问指定的类或包 | package | 包 | boolean | 基本数据类型之一,布尔类型 | byte | 基本数据类型之一,字节类型 | char | 基本数据类型之一,字符类型 | double | 基本数据类型之一,双精度浮点数类型 | float | 基本数据类型之一,单精度浮点数类型 | int | 基本数据类型之一,整数类型 | long | 基本数据类型之一,长整数类型 | short | 基本数据类型之一,短整数类型 | super | 表明当前对象的父类型的引用或者父类型的构造方法 | this | 指向当前实例对象的引用 | void | 声明当前成员方法没有返回值 | goto | 保留关键字,没有具体含义 | const | 保留关键字,没有具体含义 |
3.数据类型

3.1.数据类型转换
3.1.1自动类型转换 自动类型转换:容量小的数据类型可以自动转换为容量大的数据类型。 
注:如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值 3.1.2 强制类型转换 强制类型转换,又被称为造型,用于显式的转换一个数值的类型. 转换方式为:(type)var ,运算符“()”中的type表示将值var想要转换成的目标数据类型。 条件是转换的数据类型必须是兼容的。
double x = 3.14;
int nx = (int)x;
char c = 'a';
int d = c+1;
System.out.println(d);
System.out.println((char)d);
3.1.3.包装类过渡类型转换
- eg1:int i=Integer.parseInt(“123”)
说明:此方法只能适用于字符串转化成整型变量 - eg2: float f=Float.valueOf(“123”).floatValue()
说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。 - eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。 - eg4:double d=Double.valueOf(“123”).doublue()
说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doublue()方法返回其对应的double数值。 - eg5: long l=Long.valueOf(“123”).longValue()
说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。 - eg6: char=Character.valueOf(“123”).charValue()
说明:上例是将一个字符串转化成一个Character对象
++++++++++++++++++++++++++++++++++++++++++++++++++++++
4.常量、变量、运算符
常量
变量是什么:就是可以变化的量! 我们通过变量来操纵存储空间中的数据,变量就是指代这个存储空间!空间位置是确定的,但是里面放 置什么值不确定!Java是一种强类型语言,每个变量都必须声明其类型。
注意事项: 每个变量都有类型,类型可以是基本类型,也可以是引用类型。 变量名必须是合法的标识符。 变量声明是一条完整的语句,因此每一个声明都必须以分号结束 变量作用域 类变量(静态变量: static variable):独立于方法之外的变量,用 static 修饰。 实例变量(成员变量:member variable):独立于方法之外的变量,不过没有 static 修饰。 局部变量(lacal variable):类的方法中的变量。
变量
常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。
final 常量名=值;
final double PI=3.14;
命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰原则 : monthSalary
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则: Man, GoodMan
- 方法名:首字母小写和驼峰原则: run(), runRun()
运算符
Java 语言支持如下运算符: 算术运算符: +,-,,/,%,++,– 赋值运算符 = 关系运算符: >,<,>=,<=,==,!= instanceof 逻辑运算符: &&,||,! 位运算符: &,|,^,~ , >>,<<,>>> (了解!!!) 条件运算符 ?: 扩展赋值运算符:+=,-=,=,/=
5.java流转控制
if…else、while、do…while、for、switch…case 在这就不累述了。 跳转: return return从一个方法返回,并把控制权交给调用它的语句序;或者直接结束当前的程序; break break语句在for、while、do···while循环语句中,经常用于强行退出当前循环; continue continue语句用于跳过此次循环,执行下次循环;
6.方法
那么什么是方法呢? Java方法是语句的集合,它们在一起执行一个功能。 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最 好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。 方法的优点 使程序变得更简短而清晰。 有利于程序维护。 可以提高程序开发的效率。 提高了代码的重用性。
定义
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...c
return 返回值;
}
**修饰符:**修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。 **返回值类型 :**方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需 的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。 **方法名:**是方法的实际名称。方法名和参数表共同构成方法签名。 **参数类型:**参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参 数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。 形式参数:在方法被调用时用于接收外界输入的数据。 实参:调用方法时实际传给方法的数据。 **方法体:**方法体包含具体的语句,定义该方法的功能。
方法的重载
就是说一个类的两个方法拥有相同的名字,但是有不同的参数列表。
可变参数
在方法声明中,在指定参数类型后加一个省略号(…) 。 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声 明。
typeName... parameterName
递归
自己调用自己
7.数组
数组的定义:
数组是相同类型数据的有序集合. 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们.
数组的四个基本特点:
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量
数组声明
dataType[] arrayRefVar;
或
dataType arrayRefVar[];
创建数组
arrayRefVar = new dataType[1 arraySize];
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
三种初始化
静态初始化 除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
动态初始化 数组定义、为数组元素分配空间、赋值的操作、分开进行。
int[] a = new int[2];
a[0]=1;
a[1]=2;
数组的默认初始化 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实 例变量同样的方式被隐式初始化。
public static void main(String[] args) {
int[] a=new int[2];
boolean[] b = new boolean[2];
String[] s = new String[2];
System.out.println(a[0]+":"+a[1]);
System.out.println(b[0]+":"+b[1]);
System.out.println(s[0]+":"+s[1]);
}
数组边界
下标的合法区间:[0, length-1],如果越界就会报错;
for 和For-Each 循环
for(type element: array){
System.out.println(element);
}
for (int i = 1; i < myList.length; i++) {
System.out.println(myList[i]);
}
多维数组
type[][] typeName = new type[typeLength1][1 typeLength2];
Arrays 类
数组的工具类java.util.Arrays java.util.Arrays 类能方便地操作数组. 使用之前需要导包! 具有以下常用功能: 给数组赋值:通过 fill 方法。 对数组排序:通过 sort 方法,按升序。 比较数组:通过 equals 方法比较数组中元素值是否相等。 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。 转换为list: 通过asList(a)进行转换
8.面向对象
万物皆为对象!!!对象是抽象概念的具体实例。
以类的方式组织代码,以对象的组织(封装)数据就是面向对象
继承
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
class 父类 {
}
class 子类 extends 父类 {
}
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
为什么要继承,因为有重复。所以才继承,进而我们就知道了。父类就是公共部分的定义或规则
Java 不支持多继承(只能继承一个类),但支持多重继承。

特点
-
子类拥有父类非 private 的属性、方法。 -
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。 -
子类可以用自己的方式实现父类的方法。 -
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。 -
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)
super 与 this 关键字
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat();
super.eat();
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
animal : eat
dog : eat
animal : eat
final关键字 final 关键字声明类可以把类定义为不能继承的,即最终类;
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
class SubClass extends SuperClass{
private int n;
SubClass(){
System.out.println("SubClass");
}
public SubClass(int n){
super(300);
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300);
System.out.println("SubClass2");
}
public SubClass2(int n){
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
System.out.println("------SubClass 类继承------");
SubClass sc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
------SubClass 类继承------
SuperClass()
SubClass
SuperClass(int n)
SubClass(int n):100
------SubClass2 类继承------
SuperClass(int n)
SubClass2
SuperClass()
SubClass2(int n):200
构造函数
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
重写(Override)与重载(Overload)
重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal();
Animal b = new Dog();
a.move();
b.move();
}
}
动物可以移动
狗可以跑和走
方法的重写规则
-
参数列表与被重写方法的参数列表必须完全相同。 -
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。 -
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。 -
父类的成员方法只能被它的子类重写。 -
声明为 final 的方法不能被重写。 -
声明为 static 的方法不能被重写,但是能够被再次声明。 -
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。 -
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。 -
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。 -
构造方法不能被重写。 -
如果不能继承一个类,则不能重写该类的方法。
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重载规则:
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
多态性是对象多种表现形式的体现。
多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的三个必要条件
继承 重写 父类引用指向子类对象:Parent p = new Child();
class Shape {
void draw() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()");
}
}
虚函数
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
多态的实现方式
方式一:重写:
方式二:接口
方式三:抽象类和抽象方法
抽象类
拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明.
abstract class A{
public void fun(){
System.out.println("存在方法体的方法");
}
public abstract void print();
}
继承抽象类
我们可以通过以下方式继承 Employee 类的属性
抽象类的使用原则 (1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public; (2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理; (3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类; (4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。);
package com.wz.abstractdemo;
abstract class A{
public void fun(){
System.out.println("存在方法体的方法");
}
public abstract void print();
}
class B extends A{
@Override
public void print() {
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();
a.print();
}
}
Hello World !
封装
封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
接口
在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口与类相似点
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
-
抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。 -
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。 -
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法 -
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
[可见度] interface 接口名称 [extends 其他的接口名] {
}
import java.lang.*;
public interface NameOfInterface
{
}
接口有以下特性
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
枚举
枚举是一个特殊的类,一般表示一组常量.每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的。
enum Color
{
RED, GREEN, BLUE;
}
public class Test
{
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
}
RED
values(), ordinal() 和 valueOf() 方法
enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口。
values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:
-
values() 返回枚举类中所有的值。 -
ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。 -
valueOf()方法返回指定字符串值的枚举常量。 enum Color
{
RED, GREEN, BLUE;
}
public class Test
{
public static void main(String[] args)
{
Color[] arr = Color.values();
for (Color col : arr)
{
System.out.println(col + " at index " + col.ordinal());
}
System.out.println(Color.valueOf("RED"));
}
}
RED at index 0
GREEN at index 1
BLUE at index 2
RED
枚举成员 枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。 enum Color
{
RED, GREEN, BLUE;
private Color()
{
System.out.println("Constructor called for : " + this.toString());
}
public void colorInfo()
{
System.out.println("Universal Color");
}
}
包(package)
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包的 3 个作用如下
-
区分相同名称的类。 -
能够较好地管理大量的类。 -
控制访问范围。
定义
```java
package 包名;
Java 包的命名规则如下:
* 包名全部由小写字母(多个单词也全部小写)。
* 如果包名包含多个层次,每个层次用“.”分割。
* 包名一般由倒置的域名开头,比如 com.baidu,不要有 www。
* 自定义包不能 java 开头
**包导入**
\如果使用不同包中的其它类,需要使用该类的全名(包名+类名) example.Test test = new example.Test();
```c
\\import 包名+类名;
import example.Test;\\or
import example.*;
系统包
包 | 说明 |
---|
java.lang | Java 的核心类库,包含运行 Java 程序必不可少的系统类,如基本数据类型、基本数学函数、 字符串处理、异常处理和线程类等,系统默认加载这个包 | java.io | Java 语言的标准输入/输出类库,如基本输入/输出流、文件输入/输出、过滤输入/输出流等 | java.util | 包含如处理时间的 Date 类,处理动态数组的 Vector 类,以及 Stack 和 HashTable 类 | java.awt | 构建图形用户界面(GUI)的类库,低级绘图操作 Graphics 类、图形界面组件和布局管理 (如 Checkbox 类、Container 类、LayoutManger 接口等),以及用户界面交互控制和事 件响应(如 Event 类) | java.awt.image | 处理和操纵来自网上的图片的 Java 工具类库 | java.wat.peer | 很少在程序中直接用到,使得同一个 Java 程序在不同的软硬件平台上运行 | java.net | 实现网络功能的类库有 Socket 类、ServerSocket 类 | java.lang.reflect | 提供用于反射对象的工具 | java.util.zip | 实现文件压缩功能 | java.awt.datatransfer | 处理数据传输的工具类,包括剪贴板、字符串发送器等 | java.sql | 实现 JDBC 的类库 | java.rmi | 提供远程连接与载入的支持 | java. security | 提供安全性方面的有关支持 |
9.异常处理
异常处理的概念
是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行流程的某些特殊条件)。
关键字
Java异常机制用到的几个关键字:try、catch、finally、throw、throws。
try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常 时,异常就被抛出。 catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。 finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络 连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语 句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。 throw – 用于抛出异常。 throws – 用在方法签名中,用于声明该方法可能抛出的异常。
try{
可能会发生的异常
}catch(异常类型 异常名(变量)){
针对异常进行处理的代码
}catch(异常类型 异常名(变量)){
针对异常进行处理的代码
}...
[finally{
释放资源代码;
}]
Error与Exception区别
Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。 Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
throw与throws区别
throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化或者抛出已存在的); throw ThrowableInstance; throws:在方法的声明上使用,表示此方法在调用时必须处理异常。 throw new NullPointerException("demo"); 
? Java异常层次结构图(网上获取)
10.集合框架
所有集合类都位于 java.util 包下。Java的集合类主要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口,这两个接口又包含了一些子接口或实现类。
集合框架被设计成要满足以下几个目标:
- 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
- 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
- 对一个集合的扩展和适应必须是简单的。
集合框架都包含如下内容:
-
**接口:**是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象 -
**实现(类):**是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。 -
**算法:**是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。 
Collection是一个基本的集合接口,Collection中可以容纳一组集合元素(Element)
Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
List
List接口是一个有序, 元素可重复的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
- ArrayList
底层数据结构是数组,查改快,增删慢。
非线程安全,效率高
方法:

排序
import java.util.Collections;
Collections.sort(sites); *
- Vector
底层数据结构是数组,查改快,增删慢。
线程安全,效率低
- LinkedList
底层数据结构是链表,查改慢,增删快。
非线程安全,效率高
以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素。
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
方法:

Set
Set 接口存储一组唯一,无序的对象。
- HashSet
底层数据结构是哈希表。(无序**,唯一**)
依赖两个方法:hashCode()和equals() 保证元素唯一性
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob");
System.out.println(sites);
}
}
以上代码只会输出一个Runoob。
- LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
- TreeSet
底层数据结构是红黑树。(唯一,有序)
如何保证元素排序的呢? 自然排序,比较器排序
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。

Map与Collection是并列关系。Map提供键(key)到值(value)的映射。一个Map中不能包含相同的键,每个键只能映射一个值。
- HashMap
无序,非线程安全,效率高。HashMap允许null值(key和value都允许)。

- HashTable
无序,线程安全,效率低。除构造函数外,HashTable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。HashTable不允许null值(key和value都允许)。
- TreeMap
有序,非线程安全,效率高(O(logN)),但比不上HashMap (O(1))。
11.流(Stream)、文件(File)和IO
Java.io 包中定义了多个流类型(类或抽象类)来实现输入/输出功能;

可以从不同的角度对其进行分 类: 1.按数据流的方向不同可以分为输入流【InputStream(字节流),Reader(字符流)】和输出流【OutPutStream(字节流),Writer(字符流)】 2.按照处理数据单位不同可以分为字节流【一个字节(Byte)是8位(bit))】和字符流【一个字符是2个字节】 3.按照功能不同可以分为节点流和处理流
[图片上传失败…(image-da6cbd-1628583810189)]
4.按照操作对象分

InputStream 和 OutputStream


import java.io.*;
public class fileStreamTest {
public static void main(String[] args) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]);
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。
以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:
import java.io.*;
public class fileStreamTest2 {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
writer.append("中文输入");
writer.append("\r\n");
writer.append("English");
writer.close();
fop.close();
FileInputStream fip = new FileInputStream(f);
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
}
System.out.println(sb.toString());
reader.close();
fip.close();
}
}
Reader 流与Writer流


Reader ,Write与InputStream ,OutputStream: 唯一的区别就在于读的数据单位不同分别为(16bit),(8bit)
创建读取目录:
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
d.mkdirs();
}
}
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("目录 " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " 是一个目录");
} else {
System.out.println(s[i] + " 是一个文件");
}
}
} else {
System.out.println(dirname + " 不是一个目录");
}
}
}
删除
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
缓存流
是处理流的一种,它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,避免频繁读写硬盘, 提高了读写的效率。同时增加了一些新的方法。
BufferedReader(Reader in)
BufferedReader(Reader in,int sz)
BufferedWriter(Writer out)
BufferedWriter(Writer out,int sz)
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
BufferedOutputStream(InputStream in)
BufferedOutputStream(InputStream in,int size)
BufferedInputStream
package com.kuang.chapter;
import java.io.*;
public class TestBufferStream {
public static void main(String args[]) {
FileInputStream fis = null;
File f = new File("a.txt");
try {
fis = new FileInputStream( f);
BufferedInputStream bis = new BufferedInputStream(fis);
int c = 0;
System.out.println((char) bis.read());
System.out.println((char) bis.read());
bis.mark(100);
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
System.out.println();
bis.reset();
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
BufferedReader
package com.kuang.chapter;
import java.io.*;
public class TestBufferStream{
public static void main(String args[]){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter("a\\Student.txt"));
String s = null;
for(int i=0;i<100;i++){
s = String.valueOf(Math.random());
一个double类型的数转换成字符串
bw.write(s);
bw.newLine();
}
bw.flush();
BufferedReader br = new BufferedReader(new FileReader("a:\\Student.txt"));
while((s = br.readLine())!=null){
示已经读取到文件的末尾了。
System.out.println(s);
}
bw.close();
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
转换流
InputStreamReader 和 OutputStreamWriter 用于字节数据到字符数据之间的转换 InputStreamReader 需要和 InputStream “套接” 。 OutputStreamWriter 需要和 OutputStream “套接” 。 转换流在构造时可以指定其编码集合
import java.io.*;
public class TestTransform1 {
public static void main(String args[]) {
try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/char.txt"));
osw.write("熊方园真烦人");
System.out.println(osw.getEncoding());
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("D:\\java\\char.txt", true), "utf-8");
osw.write("不想搭理她");
System.out.println(osw.getEncoding());
osw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
数据流
数据流 DataInputStream DataOutputStream 【分别继承自InputStream 和 OutputStream】等-提供将基础数据类型写入到文件中,或者读取出来.提供了可以存取与机器无关的Java原始类型数据(int,double等)的方法。
public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try{
dos.writeDouble(Math.random());
ByteArray中
dos.writeBoolean(true);
ByteArrayInputStream bais = new
ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
dos.close();
bais.close();
}catch(Exception e){
e.printStackTrace();
}
}
打印流
打印流是输出信息最方便的类,注意包含字节打印流PrintStream和字符打印流:PrintWriter。打印流提供了非常方便的打印功能, 可以打印任何类型的数据信息,例如:小数,整数,字符串。
对象流
对象的输入输出流的作用: 用于写入对象 的信息和读取对象的信息。 使得对象持久化。 ObjectInputStream : 对象输入流 ObjectOutPutStream :对象输出流
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Student implements Serializable{
private static final long serialVersionUID = 1L;
int id ;
String name ;
transient String sex;
transient int age;
public Student(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}
}
public class objectIO {
public static void main(String[] args) throws IOException, ClassNotFoundException {
createObj();
readObj();
}
public static void createObj() throws IOException {
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream objOP = new ObjectOutputStream(fos);
Student stu = new Student("玛丽苏", "男", 18);
objOP.writeObject(stu);
objOP.close();
}
public static void readObj() throws IOException, ClassNotFoundException {
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream objIP = new ObjectInputStream(fis);
Student stu = (Student)objIP.readObject();
System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
objIP.close();
}
}
流的关闭顺序
- 一般情况下是:先打开的后关闭,后打开的先关闭
- 另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如,处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b
- 可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法。
12.多线程
进程与线程
线程的创建
继承Thread类,实现Runnable接口,实现Callable接口
1.继承Thread类
public class ThreadCreateDemo1 {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("hellow_world!");
}
}
2.实现Runnable接口
public class ThreadCreateDemo2 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("通过Runnable创建的线程!");
}
}
上述两种创建方式,工作时性质一样。但是建议使用***实现Runable接口***方式。解决单继承的局限性。
3.实现Callable接口
public class ThreadCreateDemo3 implements Callable<Integer>{
public Integer call(){
int i = 0;
for ( ; i < 100 ; i++ ){
System.out.println(Thread.currentThread().getName()+ "\t" + i);
}
return i;
}
public static void main(String[] args) {
ThreadCreateDemo3 myCallableTest = new ThreadCreateDemo3();
FutureTask<Integer> task = new FutureTask<Integer>(myCallableTest);
for (int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+ " \t" + i);
if (i == 20){
new Thread(task , "callable").start();
}
}
try{
System.out.println("callable返回值:" + task.get());
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
-
实现Callable接口,需要返回值类型 -
重写call方法,需要抛出异常 -
创建目标对象 -
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1); -
提交执行:Future result1 = ser.submit(t1); -
获取结果:boolean r1 = result1.get() -
关闭服务:ser.shutdownNow(); 总结 -
不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。 因此可以将实现Runnable接口和实现Callable接口归为一种方式。 -
Runnable、Callable接口的方式创建多线程,所以非常适合多个相同线程来处理同一份资源的情况,如果需要访问当前线程,则必须使用Thread.currentThread()方法 -
采用继承Thread类的方式创建多线程,因为线程类已经继承了Thread类,所以不能再继承其他父类
生命周期
线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态


Thread.State:
-
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。 -
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 -
阻塞(BLOCKED):表示线程阻塞于锁。 -
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。 -
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。 -
终止(TERMINATED):表示该线程已经执行完毕
线程的优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度 器按照优先级决定应该调度哪个线程来执行。 线程的优先级用数字表示,范围从1~10. hread.MIN_PRIORITY = 1; Thread.MAX_PRIORITY = 10; Thread.NORM_PRIORITY = 5; 使用以下方式改变或获取优先级 getPriority() . setPriority(int xxx)
线程方法
1 | public void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
---|
2 | public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 | 3 | public final void setName(String name) 改变线程名称,使之与参数 name 相同。 | 4 | public final void setPriority(int priority) 更改线程的优先级。 | 5 | public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 | 6 | public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 | 7 | public void interrupt() 中断线程。 | 8 | public final boolean isAlive() 测试线程是否处于活动状态。 | 9 | public static void yield() 线程礼让: 暂停当前正在执行的线程对象,并执行其他线程。 | 10 | public static void sleep(long millisec) 线程休眠: 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 | 11 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 | 12 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 | 13 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
停止线程:jdk提供了stop,但不建议使用可以自己去停止它
守护(daemon)线程
线程分为前台线程与后台线程(用户线程与守护线程) 虚拟机必须确保用户线程执行完毕 虚拟机不用等待守护线程执行完毕
并发,队列 和 锁,死锁
同一个对象被多个线程同时操作就是并发。
多个线程访问同一个对象, 并且某些线程还想修改这个对象 .这时候我们就需要线程同步 . 线程同步其实就是一种等待机制 , 多个需要同时访问此对象的线程进入这个对象的等待池 形成队列, 等待前面线程使用完毕 , 下一个线 程再使用。
上面的并发问题我们会加一个锁(synchronized)来解决。我锁上门的时候你们都别进来。但是加上锁之后会有以下为:
-
一个线程持有锁会导致其他所有需要此锁的线程挂起 ; -
在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题 ; -
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置 , 引起性能问题 . java 死锁产生的四个必要条件:
-
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 -
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。 -
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。 -
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。 死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
import java.util.Date;
public class LockTest {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args) {
LockA la = new LockA();
new Thread(la).start();
LockB lb = new LockB();
new Thread(lb).start();
}
}
class LockA implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + " LockA 开始执行");
while(true){
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + " LockA 锁住 obj1");
Thread.sleep(3000);
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + " LockA 锁住 obj2");
Thread.sleep(60 * 1000);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockB implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + " LockB 开始执行");
while(true){
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + " LockB 锁住 obj2");
Thread.sleep(3000);
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + " LockB 锁住 obj1");
Thread.sleep(60 * 1000);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果
Tue May 05 10:51:06 CST 2015 LockB 开始执行
Tue May 05 10:51:06 CST 2015 LockA 开始执行
Tue May 05 10:51:06 CST 2015 LockB 锁住 obj2
Tue May 05 10:51:06 CST 2015 LockA 锁住 obj1
解决
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class UnLockTest {
public static String obj1 = "obj1";
public static final Semaphore a1 = new Semaphore(1);
public static String obj2 = "obj2";
public static final Semaphore a2 = new Semaphore(1);
public static void main(String[] args) {
LockAa la = new LockAa();
new Thread(la).start();
LockBb lb = new LockBb();
new Thread(lb).start();
}
}
class LockAa implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockA 开始执行");
while (true) {
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockA 锁住 obj1");
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockA 锁住 obj2");
Thread.sleep(60 * 1000);
}else{
System.out.println(new Date().toString() + "LockA 锁 obj2 失败");
}
}else{
System.out.println(new Date().toString() + "LockA 锁 obj1 失败");
}
UnLockTest.a1.release();
UnLockTest.a2.release();
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockBb implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockB 开始执行");
while (true) {
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockB 锁住 obj2");
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockB 锁住 obj1");
Thread.sleep(60 * 1000);
}else{
System.out.println(new Date().toString() + "LockB 锁 obj1 失败");
}
}else{
System.out.println(new Date().toString() + "LockB 锁 obj2 失败");
}
UnLockTest.a1.release();
UnLockTest.a2.release();
Thread.sleep(10 * 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Tue May 05 10:59:13 CST 2015 LockA 开始执行
Tue May 05 10:59:13 CST 2015 LockB 开始执行
Tue May 05 10:59:13 CST 2015 LockB 锁住 obj2
Tue May 05 10:59:13 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:14 CST 2015LockB 锁 obj1 失败
Tue May 05 10:59:14 CST 2015LockA 锁 obj2 失败
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj2
- synchronized 与 Lock 的对比
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放 - Lock只有代码块锁,synchronized有代码块锁和方法锁使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方 法体之外)
线程通讯
线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。
线程的通信方式
- volatile
- Wait/Notify机制
- join方式
- threadLocal
- CountDownLatch 并发工具
- CyclicBarrier 并发工具
volatile
public class Volatile implements Runnable {
private static volatile Boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + " - 执行");
}
System.out.println("线程结束");
}
public static void main(String[] args) {
Thread t = new Thread(new Volatile());
t.start();
try {
Thread.sleep(5);
flag = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
线程结束
**WaitNotify **
public class WaitNotify {
private static Object lock = new Object();
private static Integer i = 0;
public void odd() {
while (i < 10) {
synchronized (lock) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void even() {
while (i < 10) {
synchronized (lock) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
WaitNotify waitNotify = new WaitNotify();
Thread t1 = new Thread(() -> waitNotify.odd(), "线程1");
Thread t2 = new Thread(() -> waitNotify.even(), "线程2");
t1.start();
t2.start();
}
}
join
package threadCommunication;
public class JoinTest extends Thread {
@Override
public void run() {
try {
int sleepTime = (int) (Math.random() * 1000);
System.out.println(sleepTime);
Thread.sleep(sleepTime);
System.out.println("JoinTest end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
JoinTest j = new JoinTest();
j.start();
j.join();
System.out.println("main end");
}
threadLocal
package sync;
public class SequenceNumber {
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
public Integer initialValue() {
return 0;
}
};
public int getNextNum() {
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
private static class TestClient extends Thread {
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this.sn = sn;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("thread[" + Thread.currentThread().getName() + "] sn[" + sn.getNextNum() + "]");
}
}
}
public static void main(String[] args) {
SequenceNumber sn = new SequenceNumber();
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
}
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
**CountDownLatch **CountDownLatch可以代替 wait/notify的使用,并去掉 synchronized
import java.util.concurrent.CountDownLatch;
public class CountDown {
private static Integer i = 0;
final static CountDownLatch countDown = new CountDownLatch(1);
public void odd() {
while (i < 10) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
countDown.countDown();
} else {
try {
countDown.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void even() {
while (i < 10) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
countDown.countDown();
} else {
try {
countDown.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
CountDown countDown = new CountDown();
Thread t1 = new Thread(() -> countDown.odd(), "线程1");
Thread t2 = new Thread(() -> countDown.even(), "线程2");
t1.start();
t2.start();
}
}
CyclicBarrier
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准备...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部启动完毕!");
}, "线程1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准备...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部启动完毕!");
}, "线程2").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准备...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部启动完毕!");
}, "线程3").start();
}
}
线程3: 准备...
线程2: 准备...
线程1: 准备...
全部启动完毕!
全部启动完毕!
全部启动完毕!
线程池
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
ExecutorService 和 Executors
-
**ExecutorService:**真正的线程池接口。常见子类ThreadPoolExecutor void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执 行Runnable -
Future submit(Callable task):执行任务,有返回值,一般又来执行 Callable -
void shutdown() :关闭连接池
**Executors:**工具类、线程池的工厂类,用于创建并返回不同类型的线程池
13.注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
作用
不是程序本身 , 可以对程序作出解释.(这一点和注释(comment)没什么区别)
可以被其他程序(比如:编译器等)读取.
可以附加在package , class , method , field 等上面 , 相当于给他们添加了额外的辅助信息
我们可以通过反射机制实现对这些元数据的访问
格式
注解是以"@注释名"在代码中存在的
还可以添加一些参数值 , 例如:@SuppressWarnings(value=“unchecked”)
内置注解

Java 定义了一套注解,共有 10 个,java7之前3 个在 java.lang 中, 4 个在 java.lang.annotation 中,后续增加三个
作用在代码的注解( java.lang )是
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是( java.lang.annotation)注解
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
java7之后增加的注解
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
package com.annotation;
import java.util.ArrayList;
import java.util.List;
public class Test1 extends Object {
@Override
public String toString() {
return super.toString();
}
@Deprecated
public static void stop(){
System.out.println("测试 @Deprecated");
}
@SuppressWarnings("all")
public void sw(){
List list = new ArrayList();
}
public static void main(String[] args) {
stop();
}
}
元注解
package com.annotation;
import java.lang.annotation.*;
public class Test2 {
@MyAnnotation
public void test(){
}
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface MyAnnotation{
}
自定义注解
-
使用 @interface自定义注解时 , 自动继承了java.lang.annotation.Annotation接口 @ interface用来声明一个注解 , 格式 : public @ interface 注解名 { 定义内容 } -
其中的每一个方法实际上是声明了一个配置参数. 方法的名称就是参数的名称. -
返回值类型就是参数的类型 ( 返回值只能是基本类型,Class , String , enum ). 可以通过default来声明参数的默认值 -
如果只有一个参数成员 , 一般参数名为value -
注解元素必须要有值 , 我们定义注解元素时 , 经常使用空字符串,0作为默认值 .
注解参数的可支持数据类型: 1.所有基本数据类型(int,float,boolean,byte,double,char,long,short) 2.String类型 3.Class类型 4.enum类型 5.Annotation类型 6.以上所有类型的数组
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;
}
package annotation;
import annotation.FruitColor.Color;
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void displayName(){
System.out.println("水果的名字是:苹果");
}
}
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
public int id() default -1;
public String name() default "";
public String address() default "";
}
14.反射
反射机制

Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method; 其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组 成部分。
获取类的方法
Class clazz = String.class;
Person p = new Person();
Class clazz = p.getClass();
String className = “java.util.commons”;
Class clazz = Class.forName(className);
String className = “java.util.commons”;
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);
得到构造器的方法
Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,
Constructor[] getConstructors() -- 获得类的所有公共构造函数
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
获取字段
Field getField(String name) -- 获得命名的公共字段
Field[] getFields() -- 获得类的所有公共字段
Field getDeclaredField(String name) -- 获得类声明的命名的字段
Field[] getDeclaredFields() -- 获得类声明的所有字段
获取方法的信息
Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法
Method[] getMethods() -- 获得类的所有公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
Method[] getDeclaredMethods() -- 获得类声明的所有方法
通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
package com.ys.reflex;
public class Person {
private String name = "Tom";
public int age = 18;
public Person() {
}
private void say(){
System.out.println("private say()...");
}
public void work(){
System.out.println("public work()...");
}
}
String className = c2.getName();
System.out.println(className);
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());
}
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());
}
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());
}
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());
}
Field f1 = c2.getField("age");
System.out.println(f1);
Field f2 = c2.getDeclaredField("name");
f2.setAccessible(true);
System.out.println(f2);
Object p2 = c2.newInstance();
f2.set(p2,"Bob");
System.out.println(f2.get(p2));
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());
}
反射方法执行
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws Exception{
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
Class clz = Class.forName("com.wyl.api.Apple");
Method setPriceMethod = clz.getMethod("setPrice", int.class);
Constructor appleConstructor = clz.getConstructor();
Object appleObj = appleConstructor.newInstance();
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}
|