Java经典面试题(二)

1. static的用法
  1. 修饰成员变量:
    static修饰的类的成员变量不再属于成员了,而是属于类的,因为实例出来的对象并不能随意的更改他们。一旦static修饰的成员变量被修改了那么所有的实例对象都会受到影响
  2. 修饰成员方法:
    static修饰的成员方法可以使用类名.方法名来调用,避免了要先new一个新对象的资源消耗,常用于工具类。被static修饰的成员方法里面不能使用非static修饰的成员变量和方法。
  3. 静态块:
    在初始化一个新的对象的时候static块会先执行。甚至可以在还没有创建对象的时候初始化。而且在程序运行的过程中只需要初始化一次就行了,在new新对象的时候不必再重复执行static块里面的内容。
  4. 静态导包:
    import static java.util.Math;如果这样导包的话不必写类名来调用里面的方法,而是可以直接使用方法名来调用类方法
2. final的用法
  1. 类:意味着绝育了,被final声明的类不能再派生出新的子类,不能作为父类被继承,因此一个类不能既是abstract又是final
  2. 变量:可以保证他们在使用中不会被改变,被声名为final的变量必须在new一个对象的时候初始化,在以后的引用中只能读取不能修改
  3. 方法:意味着该方法只能使用,而不能在子类中被重写
3. String, StringBuilder, StringBuffer的区别
  1. 在java中提供了String,StringBuilder和StringBuffer来表示和操作字符串。字符串就是多个字符的集合。

  2. String是内容不可变的字符串。
    String s = new String("sss");
    String底层使用了一个不可变的字符数组(public final char value[])

  3. StringBuilder和StringBuffer是内容可以改变的字符串
    两者底层使用的是可变的字符数组(char[] value)

  4. append(字符串拼接)方法:
    1⃣️String c = "a"+"b";
    2⃣️StringBuilder sb = new StringBuilder(); sb.append("a").append("b");
    方法二的效率会更高,因为方法一会创建大量的空间对象

  5. 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
	```
  1. 此处的Integer a = 127; 实际上是完成了一个自动装箱的过程也就是其实执行的是Integer a = Integer.valueOf(127); 而查看valueOf的源码可以观察到[low, high]内的数字都是直接从IntegerCache中直接取出的。
  2. IntegerCache的源码中可以看到low = -128, high = 127; 所以可以得出[-128,127]内的数字都已经被存储到了cache数组中。而这些操作是在IntegerCache里面的静态块中实现的。
  3. 在Integer.class装载的过程中就已经实例化了-128~127的Integer类型对象放入了cache数组里,所以在自动装箱的时候可以直接返回cache中暂存的Integer对象。
  4. 因为127就在cache数组内,所以a和b都是在引用cache里面的内容而已,而==比较的是地址,所以 a == b是成立的,他们都在静态区。但是超过了127的时候就只能在对象区创建,所以地址肯定是不一样的,因此c == d 是不成立的。
  5. 如果想让后一个判断返回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的内存空间

在这里插入图片描述

  1. 堆(Heap):
    堆是JVM管理的最大的一块内存区域, 也是被所有线程所共享的内存区域,该内存区存放了对象的实例以及数组。堆也被称为GC堆,因为GC的主要对象就是Heap。GC堆中还分为老年区和新生区(Eden,Survivor(form), Survivor(to))。新生区中三者的空间比是8:1:1。
  2. 方法区(Method Area):
    方法区也被成为永久代,它用于存储虚拟机加载的类信息,常量,静态变量,是各个线程共享的内存区域。永久代的GC和老年代是捆绑在一起的,因此不管是两者谁满了都会调用老年代的GCJKD7中已经将其转入了Heap中
  3. Java 虚拟机栈(JVM Stack)
    描述了java方法执行的内存模型,每个方法在执行的时候都会建立一个栈帧,用于存储局部变量,操作栈,方法出口等信息。栈是线程私有的。
    局部变量表中存放了基本数据类型,对象引用(指针不是对象本身)。
  4. 本地方法栈(Native Stack)
    为Native方法服务
  5. 程序计数器(PC Register)
    当前线程所执行的字节码的行号指示器
6. 守护线程和非守护线程
  1. 守护线程就是程序在运行的时候在后台提供的一种通用服务的线程,他们是程序中国呢不可或缺的一部分。比如GC线程就是一个守护线程。
  2. 非守护线程就是被安排的那部分线程,当所有的非守护线程都结束了那么此时程序终止,因为守护线程没有要守护的对象了。
7. 创建线程的四种方式
  1. 继承Thread类来创建线程
		public class MyThread extends Thread{
			public void run(){
		
			}
		}
		public class Main{
			public static void main(String [] args){
				new MyThread.start();
			}
		}
		
  1. 实现Runnable接口创建线程
		public class MyThread implements Runnable{
			public void run(){}
		}
		public class Main{
			public static void main(String args[]){
				new Thread(new MyThread()).start();
			}
		}
  1. 使用Callable和Future创建线程

    1. 重写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;
      	}
      }
      
    2. 创建线程池运行线程,使用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. 导致线程阻塞的原因
  1. sleep()方法:sleep方法会将处于running状态的线程在指定时间之内进入blocked状态,期间他所占有的临界资源并不会释放,也就是不会释放锁。
  2. wait()方法:wait方法常常和notify以及notifyall方法一起用。wait会将处于running的的线程放入等待队列中,如果wait方法中有参数则一段时间内会自己重新进入锁池等待取得同步锁;如果wait方法没有参数,则需要notify以及notifyall来唤醒进入锁池,取得同步锁之后即可进入runnable状态。wait和sleep的不同之处是wait方法在进入等待队列的时候会释放锁, 因此wait方法也只能在sync块中使用。
  3. yield()方法:yield方法表示当前线程愿意让出CPU,但是最终的决定权在调度器手中。
  4. suspend()和resume()方法:这两个方法配套使用,suspend用来使线程进入阻塞态,而且不会自动回复。resume方法用来让线程重新进入runnable状态。
  5. join()方法:join会将其他线程都阻塞,只有调用join方法的线程执行结束之后才能继续执行。相当于把并行改成了串行。
9. 单例模式

单例模式是常用的设计模式之一,他只允许一个实例的存在。
所谓懒汉和饿汉的区别就是:懒汉式在真正的时候建立这个实例对象;饿汉式就是不管你用不用一开始就会创建这个类的实例。

  1. 饿汉式(静态代码块)

    public class Singleton {
        private static Singleton instance;
        static{
            instance = new Singleton();
        }
        private Singleton(){}
        public static Singleton getInstance(){
            return instance;
        }
    }
    
  2. 饿汉式(静态常量)

    public class Singleton {
        private final static Singleton INSTANCE = new Singleton();
        private Singleton(){}
        public static Singleton getInstance(){
            return INSTANCE;
        }
    }
    
  3. 懒汉式(效率低)

    public class Singleton {
        private static Singleton singleton;
        private Singleton() {}
        public static synchronized Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
  4. 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;
        }
    }
    
  5. 静态内部类

    public class Singleton {
        private Singleton(){}
        private static class SingletonInstance{
            private static final Singleton INSTANCE = new Singleton();
        }
        public static Singleton getInstance(){
            return SingletonInstance.INSTANCE;
        }
    }
    
  6. 枚举

    public enum SingletonEnum {
        INSTANCE;
        public void whateverMethod(){}
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值