文章目录
1. static的用法
- 修饰成员变量:
static修饰的类的成员变量不再属于成员了,而是属于类的,因为实例出来的对象并不能随意的更改他们。一旦static修饰的成员变量被修改了那么所有的实例对象都会受到影响 - 修饰成员方法:
static修饰的成员方法可以使用类名.方法名来调用,避免了要先new一个新对象的资源消耗,常用于工具类。被static修饰的成员方法里面不能使用非static修饰的成员变量和方法。 - 静态块:
在初始化一个新的对象的时候static块会先执行。甚至可以在还没有创建对象的时候初始化。而且在程序运行的过程中只需要初始化一次就行了,在new新对象的时候不必再重复执行static块里面的内容。 - 静态导包:
import static java.util.Math;如果这样导包的话不必写类名来调用里面的方法,而是可以直接使用方法名来调用类方法
2. final的用法
- 类:意味着绝育了,被final声明的类不能再派生出新的子类,不能作为父类被继承,因此一个类不能既是abstract又是final
- 变量:可以保证他们在使用中不会被改变,被声名为final的变量必须在new一个对象的时候初始化,在以后的引用中只能读取不能修改
- 方法:意味着该方法只能使用,而不能在子类中被重写
3. String, StringBuilder, StringBuffer的区别
-
在java中提供了String,StringBuilder和StringBuffer来表示和操作字符串。字符串就是多个字符的集合。
-
String是内容不可变的字符串。
String s = new String("sss");
String底层使用了一个不可变的字符数组(public final char value[]) -
StringBuilder和StringBuffer是内容可以改变的字符串
两者底层使用的是可变的字符数组(char[] value) -
append(字符串拼接)方法:
1⃣️String c = "a"+"b";
2⃣️StringBuilder sb = new StringBuilder(); sb.append("a").append("b");
方法二的效率会更高,因为方法一会创建大量的空间对象 -
StringBuilder是线程不安全的, 效率较高 public StringBuilder append
StringBuffer是线程安全的, 效率较低 public synchronized StringBuffer append
4. Integer比较127和128
```
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
sout(a == b); //true
sout(c == d); //false
```
- 此处的Integer a = 127; 实际上是完成了一个自动装箱的过程也就是其实执行的是Integer a = Integer.valueOf(127); 而查看valueOf的源码可以观察到[low, high]内的数字都是直接从IntegerCache中直接取出的。
- IntegerCache的源码中可以看到low = -128, high = 127; 所以可以得出[-128,127]内的数字都已经被存储到了cache数组中。而这些操作是在IntegerCache里面的静态块中实现的。
- 在Integer.class装载的过程中就已经实例化了-128~127的Integer类型对象放入了cache数组里,所以在自动装箱的时候可以直接返回cache中暂存的Integer对象。
- 因为127就在cache数组内,所以a和b都是在引用cache里面的内容而已,而==比较的是地址,所以 a == b是成立的,他们都在静态区。但是超过了127的时候就只能在对象区创建,所以地址肯定是不一样的,因此c == d 是不成立的。
- 如果想让后一个判断返回true可以将==更换为equals, 因为Integer已经重写了equals方法,比较的是值。
//1
Integer a=new Integer(123);
Integer b=new Integer(123);
System.out.println(a==b);//输出 false
//2
Integer c=123;
Integer d=123;
System.out.println(c==d);//输出 true
//3
Integer e=129;
Integer f=129;
System.out.println(e==f);//输出 false
//4
int g=59;
Integer h=new Integer(59);
System.out.println(g==h);//输出 true,自动拆箱了:g == h.IntValue()
5. JVM的内存空间
- 堆(Heap):
堆是JVM管理的最大的一块内存区域, 也是被所有线程所共享的内存区域,该内存区存放了对象的实例以及数组。堆也被称为GC堆,因为GC的主要对象就是Heap。GC堆中还分为老年区和新生区(Eden,Survivor(form), Survivor(to))。新生区中三者的空间比是8:1:1。 - 方法区(Method Area):
方法区也被成为永久代,它用于存储虚拟机加载的类信息,常量,静态变量,是各个线程共享的内存区域。永久代的GC和老年代是捆绑在一起的,因此不管是两者谁满了都会调用老年代的GC。JKD7中已经将其转入了Heap中 - Java 虚拟机栈(JVM Stack)
描述了java方法执行的内存模型,每个方法在执行的时候都会建立一个栈帧,用于存储局部变量,操作栈,方法出口等信息。栈是线程私有的。
局部变量表中存放了基本数据类型,对象引用(指针不是对象本身)。 - 本地方法栈(Native Stack)
为Native方法服务 - 程序计数器(PC Register)
当前线程所执行的字节码的行号指示器
6. 守护线程和非守护线程
- 守护线程就是程序在运行的时候在后台提供的一种通用服务的线程,他们是程序中国呢不可或缺的一部分。比如GC线程就是一个守护线程。
- 非守护线程就是被安排的那部分线程,当所有的非守护线程都结束了那么此时程序终止,因为守护线程没有要守护的对象了。
7. 创建线程的四种方式
- 继承Thread类来创建线程
public class MyThread extends Thread{
public void run(){
}
}
public class Main{
public static void main(String [] args){
new MyThread.start();
}
}
- 实现Runnable接口创建线程
public class MyThread implements Runnable{
public void run(){}
}
public class Main{
public static void main(String args[]){
new Thread(new MyThread()).start();
}
}
-
使用Callable和Future创建线程
-
重写call方法,可抛出异常,可返回值
class MyThread implements Callable<Integer>{ private String name; private long time; private boolean flag; private int step; public Race(String name, long time){ this.name = name; this.time = time; } @Override public Integer call() throws Exception{ while(flag){ Thread.sleep(time); step++; } return step; } }
-
创建线程池运行线程,使用get方法等到操作结束了之后返回值。
public static void main(String args[]){ ExecutorService = ser = new Executor.newFixThreadPool(2); MyThread thread1 = new MyThread("racer1", 1000); MyThread thread2 = new MyThread("racer2", 500); Future<Integer> result1 = ser.submit(racer1); Future<Integer> result2 = ser.submit(racer2); Thread.sleep(2000); result1.setFlag = false; result2.setFlag = false; int num1 = result1.get(); int num2 = result2.get(); sout(num1); //2 sout(num2); //4 ser.shutdownNow(); }
-
8. 导致线程阻塞的原因
- sleep()方法:sleep方法会将处于running状态的线程在指定时间之内进入blocked状态,期间他所占有的临界资源并不会释放,也就是不会释放锁。
- wait()方法:wait方法常常和notify以及notifyall方法一起用。wait会将处于running的的线程放入等待队列中,如果wait方法中有参数则一段时间内会自己重新进入锁池等待取得同步锁;如果wait方法没有参数,则需要notify以及notifyall来唤醒进入锁池,取得同步锁之后即可进入runnable状态。wait和sleep的不同之处是wait方法在进入等待队列的时候会释放锁, 因此wait方法也只能在sync块中使用。
- yield()方法:yield方法表示当前线程愿意让出CPU,但是最终的决定权在调度器手中。
- suspend()和resume()方法:这两个方法配套使用,suspend用来使线程进入阻塞态,而且不会自动回复。resume方法用来让线程重新进入runnable状态。
- join()方法:join会将其他线程都阻塞,只有调用join方法的线程执行结束之后才能继续执行。相当于把并行改成了串行。
9. 单例模式
单例模式是常用的设计模式之一,他只允许一个实例的存在。
所谓懒汉和饿汉的区别就是:懒汉式在真正的时候建立这个实例对象;饿汉式就是不管你用不用一开始就会创建这个类的实例。
-
饿汉式(静态代码块)
public class Singleton { private static Singleton instance; static{ instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return instance; } }
-
饿汉式(静态常量)
public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } }
-
懒汉式(效率低)
public class Singleton { private static Singleton singleton; private Singleton() {} public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
-
Double Check
public class Singleton { private static volatile Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
-
静态内部类
public class Singleton { private Singleton(){} private static class SingletonInstance{ private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return SingletonInstance.INSTANCE; } }
-
枚举
public enum SingletonEnum { INSTANCE; public void whateverMethod(){} }