Java基础

Java的四种引用方式

因为值类型存储于Stack里,引用类型存储于Heap里,但是在Stack里有指向Heap的引用。

Java简单数据类型没有String。

简单数据类型具有栈共享。int a = 3;int b = 3;那么a和都储存在 栈里且指向同一个字面值3。但是对a,b的分别修改是不会导致对方数值的变化。

Java的简单数据类型作为参数时传递的是值(Pass by Value),在函数中使用的是源变量的拷贝,对源变量不产生影响。

引用是一种数据类型(保存在stack),保存了对象在内存(heap)中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象)。

强引用(StrongReference)

是指创建一个对象并把这个对象赋给一个引用变量。

比如:

Object object = new Object();

String str = "hello";

软引用(SoftReference)

1
2
3
4
5
6
//创建对象强引用
MyObject aRef = new MyObject();
//参数调用
SoftReference aSoftRef=new SoftReference(aRef);
//结束强引用 为软引用
aRef = null;

弱引用(WeakReference)

1
2
3
4
5
6
7
8
9
10
11
12
//用java.lang.ref.WeakReference类来表示。
//创建弱引用对象
WeakReference<People> reference=new WeakReference<People>(new People("zhangsan"));
//一旦gc执行则马上被回收
System.gc();

//特殊情况
//关联强引用
People people=new People("zhangsan");
WeakReference<People>reference=new WeakReference<People>(people);
//不会回收
System.gc();

虚引用(PhantomReference)

1
2
3
4
5
//用java.lang.ref.PhantomReference类来表示。
//虚引用必须和引用队列关联使用。
//只要进行垃圾回收,虚引用就会被回收。
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);

垃圾回收顺序为 虚引用—>弱引用—>软引用—>强引用。

弱引用在没有强引用被执行gc时马上回收。软引用在没有强引用且内存不足被执行gc时回收。

Java的垃圾回收机制

垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存。

内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为对象游离

Java的类反射机制

.getClass( ) .class Class.forName( )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
//第一种方式获取Class对象(有对象反射多此一举)
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());

//第二种方式获取Class对象(需要导入类包依赖性太强)
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

//第三种方式获取Class对象(推荐使用)
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

}

Class.forName( )对比

Class.forName( )ClassLoader.loadClass( )区别:

Class.forName( )默认初始化执行static方法。

类反射代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//类加载
Class clazz = Class.forName("fanshe.Student");

//反射获取构造方法
//获取所有公有构造方法
Constructor[] conArray = clazz.getConstructors();
//所有构造方法
clazz.getDeclaredConstructors();
//所有公有无参
clazz.getConstructor(null);

//Class.newInstance()和Class.Constructor.newInstance()的区别
Class.newInstance() //只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() //可以根据传入的参数,调用任意构造构造函数。

Class.newInstance() //抛出所有由被调用构造函数抛出的异常。

Class.newInstance() //要求被调用的构造函数是可见的,也即必须是public类型的;
Constructor.newInstance() //在特定的情况下,可以调用私有的构造函数。

//反射获取成员变量
.getFields()

//反射获取方法
Method m = clazz.getMethods("方法名",String.class)//第二个参数是原方法形参
//实例化一个Student对象
Object obj = claszz.getConstructor().newInstance();
//反射调用方法
m.invoke(obj, "方法参数");

Java重要关键字

访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
* 成员变量/方法的访问权限

* private default protected public

* 自己包自己类 √ √ √ √

* 自己包别的类 √ √ √

* 别的包别的类有继承关系② ① √

* 别的包别的类无继承关系 √

①:子类可以继承,但是不能访问父类的成员变量/方法(一般来说,可以访问就可以继承)。

②:有继承关系说明访问对象所在的类是父类。

private

只能在声明 private(内部)类、方法或字段的类中引用这些类、方法或字段。在类的外部或者对于子类而言,它们是不可见的。 所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

protected

可以在声明 protected 类、方法或字段的类、同一个包中的其他任何类以及任何子类(无论子类是在哪个包中声明的)中引用这些类、方法或字段。所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

public

可能只会在其他任何类或包中引用 public 类、方法或字段。所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

类 方法 变量修饰符

abstract

  • 不能直接实例化。
  • 方法不在声明它的类中实现,但必须在某个子类中重写。
  • 采用 abstract 方法的类是抽象类,必须声明为abstract。

class

  • 用来声明新的类,该类是相关变量和/或方法的集合。
  • 类是对象的模板,每个对象都是类的一个实例。
  • 要使用类,通常使用 new 操作符将类的对象实例化,然后调用类的方法来访问类的功能。

extends

  • 用在 class 或 interface 声明中,用于指示所声明的类或接口是子类。
  • 子类继承父类的所有 public 和 protected 变量和方法。
  • 子类可以重写父类的任何非 final 方法。

final

  • 可以应用于类,以指示不能扩展该类(不能有子类)。
  • final 关键字可以应用于方法,以指示在子类中不能重写此方法。
  • 一个类不能同时是 abstract 又是 final。abstract 意味着必须扩展类,final 意味着不能扩展类。abstract 意味着必须重写方法,final 意味着不能重写方法。

implements

  • 在 class 声明中使用,以指示所声明的类提供了指定的接口中所声明的所有方法的实现。
  • 类必须提供在接口中所声明的所有方法的实现。
  • 一个类可以实现多个接口。

interface

  • 用来声明新的 Java 接口,接口是方法的集合。
  • 接口是 Java 语言的一项强大功能。任何类都可声明它实现一个或多个接口,这意味着它实现了在这些接口中所定义的所有方法。
  • 实现了接口的任何类都必须提供在该接口中的所有方法的实现。一个类可以实现多个接口。

native

  • 可以应用于方法,以指示该方法是用 Java 以外的语言实现的。

static

  • static 关键字可以应用于内部类(在另一个类中定义的类)、方法或字段(类的成员变量)。
  • 意味着应用它的实体在声明该实体的类的任何特定实例外部可用。
  • static 字段(类的成员变量)在类的所有实例中只存在一次。
  • 可以从类的外部调用 static 方法,而不用首先实例化该类。
  • 这样的引用始终包括类名作为方法调用的限定符。

strictfp

  • 使用它来声明一个类、接口或者方法时,那么所声明的范围内Java的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果想让浮点运算更加精确,而且不会因为不同的硬件平台所执行的结果不一致的话,那就请用关键字strictfp。
  • 可以将一个类、接口以及方法声明为strictfp,但是不允许对接口中的方法以及构造函数声明strictfp关键字。

synchronized

  • 可以应用于方法或语句块,并为一次只应由一个线程执行的关键代码段提供保护。
  • 可防止代码的关键代码段一次被多个线程执行。
  • 如果应用于静态方法,那么,当该方法一次由一个线程执行时,整个类将被锁定。
  • 如果应用于实例方法,那么,当该方法一次由一个线程访问时,该实例将被锁定。
  • 如果应用于对象或数组,当关联的代码块一次由一个线程执行时,对象或数组将被锁定。

变量引用

super

  • 用于引用使用该关键字的类的超类。
  • 作为独立语句出现表示调用超类的构造方法。

this

  • 当引用可能不明确时,可以使用它来引用当前的实例。

void

  • void 关键字表示 null 类型。
  • void 可以用作方法的返回类型,以指示该方法不返回值。

保留字

goto

  • 保留关键字,但无任何作用。

const

  • 类型修饰符,使用它声明的对象不能更新。

Java.IO

java.io.File

File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。

public File(String pathname)

以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

public File(String parent,String child)

以parent为父路径,child为子路径创建File对象。

流的分类

(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
InputStream:
int read(byte[] b);
Reader:
int read(char[] c);
---------------------
OutputStream:
void write(int b/int c);
void write(byte[] b/char[] cbuf);
void write(byte[] b/char[] buff, int off, int len);
void flush();
void close(); //需要先刷新,再关闭此流
Writer:
void write(String str);
void write(String str, int off, int len);

文件流

读取文件

1
2
3
4
5
6
//建立一个流对象,将已存在的一个文件加载进流。
FileReader fr = new FileReader("文件名.后缀");
//创建一个临时存放数据的数组。
char[] ch = new char[1024];
//调用流对象的读取方法将流中的数据读入到数组中。
fr.read(ch);

写入文件

1
2
3
4
5
6
//创建流对象,建立数据存放文件
FileWriter fw = new FileWriter("文件名.后缀");
//调用流对象的写入方法,将数据写入流
fw.write("要写入的数据");
//关闭流资源,并将流中的数据清空到文件中。
fw.close();

缓冲流

对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
BufferedReader br = null;
BufferedWriter bw = null;
try {
//step1:创建缓冲流对象:它是过滤流,是对节点流的包装
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\destBF.txt"));
String str = null;
//一次读取字符文本文件的一行字符
while ((str = br.readLine()) != null) {
bw.write(str); //一次写入一行字符串
bw.newLine(); //写入行分隔符
}
bw.flush(); //step2:刷新缓冲区
} catch (IOException e) {
e.printStackTrace();
}
finally {
// step3: 关闭IO流对象
try {
if (bw != null) {
bw.close(); //关闭过滤流时,会自动关闭它所包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

转换流

public InputSreamReader(InputStream in,String charsetName);

字节数组解码成字符串。

public OutputStreamWriter(OutputStream out,String charsetName);

字符串编码成字节数组。

标准输入输出流

System.in的类型是InputStream。

System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
System.out.println("请输入信息(退出输入e或exit):");
//把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { //读取用户输入的一行数据 --> 阻塞程序
if (s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")) {
System.out.println("安全退出!!");
break;
}
//将读取到的整行字符串转成大写输出
System.out.println("-->:"+s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); //关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}

对象流

序列化

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  • 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
  • 如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
    • Serializable
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
    • private static final long serialVersionUID;
    • serialVersionUID用来表明类的不同版本间的兼容性
    • 如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明
  • 显示定义serialVersionUID的用途
    • 希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
    • 不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID
1
2
3
4
5
6
7
8
9
10
11
//序列化对象韩梅梅。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test3.txt"));
Person p = new Person("韩梅梅",18,"中华大街",new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:将磁盘中的对象数据源读出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test3.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();

Java 集合类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Collection
|-- List
| |-- ArrayList //数组/线程不安全
| |-- Vector //数组/线程安全
| |-- LinkedList //链表
|-- Set
| |-- HashSet //基于hashmap
| |-- LinkedHashSet //基于hashset
| |-- TreeSet //红黑树
Map
|-- --- HashMap //链表+数组(JDK1.8 >8红黑树)/线程不安全/拉链法/可null
|-- --- HashTable //线程安全(Synchronized)/键值都不可为null
|-- --- LinkedHashMap //基于hashmap
|-- --- TreeMap //红黑树

ConcurrentHashMap //线程安全/分段锁/JDK1.8 Node数组+链表+红黑树/synchronized CAS 。

Java 锁

乐观锁

  • 在更新的时候会判断一下在此期间别人有没有去更新这个数据。
  • 适用于多读的应用类型,这样可以提高吞吐量。
  • 乐观锁的实现方式CAS、数据版本。
  • CAS(Compare and Swap 比较并交换)CAS操作中包含三个操作数——内存数据(V)、预期原值(A)、拟写入值(B)。如果V == A,那么更新V为新值B,否则不做任何操作。

悲观锁

  • 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
  • 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
  • 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

公平锁/非公平锁

  • 公平锁是指多个线程按照申请锁的顺序来获取锁。
  • 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。

偏向锁/轻量级锁/重量级锁

  • 针对Synchronized。
  • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
  • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。

Synchronized

  • 可重入锁。
  • 非公平锁。
  • 悲观锁 。
  • 排他锁。

ReenTrantLock

  • 可重入锁 。
  • 可设置为公平锁。
  • 排他锁。