Java经典面试题(一)

1. 多态的实现:
		接口,重写父类的方法,重载
2. 抽象类和接口的区别:
1. 默认方法:抽象类有默认的方法实现;java8之前接口不存在方法的实现。
2. 实现方法:extends来继承抽象类,若字类不是抽象的则需实现抽象类中声明的方法;接口的话,子类用implements来实现接口,需要实现接口定义的所有方法。
3. 构造器:抽象类可以有构造器;接口不能。
4. 和正常类的区别:抽象类不能被实例化;接口与类的类型不同。
5. 访问修饰符:抽象方法:public, protected. default等修饰符
			 接口:默认是public,不能使用其他
6. 多继承:抽象类一个字类只能有一个父类;接口的话一个字类能实现多个接口
7. 添加新方法:抽象类的子类中的代码可以不更改;接口则必须在子类中实现新方法。
3. 抽象类的注意点:
1. 抽象类不能被实例化
2. 抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类
3. 抽象方法只声明就好,不需要在抽象类中实现
4. 构造方法,类方法(用static修饰的方法)不能声明为抽象方法
5. 抽象类的子类只有两个选择:继续抽象或者实现父类的抽象方法
4. 不可变对象:
1. 对象一旦被创建就无法改变,任何对他的改变都只会产生一个新的对象
2. 不可变对象的类即不可变类,如String, 基本类型的包装类, BigInteger, BigDecimal等
3. 不可变类应该被声明为final,或者使用静态工厂并声明构造器为private;
   如果成员属性是可变对象属性,不要使这些对象改变
5. 静态工厂方法:
1. 获得类的实例的最简单的方法就是new,通过构造器来实现对象的创建。但不通过new,而是通过静态方法来创建类的实例对象,这就是静态工厂方法。
2. 相比于new来实例,静态工厂方法是有名字的:newInstance, valueOf, getInstance...
3. 相比于new来实例,静态工厂方法不用每次被调用时都创建新对象(单例模式)
4. 相比于new来实例,静态工厂方法可以返回原返回类型的子类
5. 相比于new来实例,静态工厂方法在创建带泛型的实例时能够使代码简洁
   Map<String, Date> map = new HashMap<String, Date>();		F
   Map<String, Date> map = new HashMap<>();					T
6. 静态工厂方法可以创建参数一样但是名称不一样的工厂方法, 例如:

    class Child{
   		int age;
   		int weight;
   		public Child(int age, int weight){
   			this.age = age;
   			this.weight = weight;
   		}
   		public Child(int age){
   			this.age = age;
   		}
    }
   此时如果需要加入参数只有weight的构造方法显然会报错,因为和第二个构造方法的参数类型一样,会被认为是两个一样的构造器
	 如果用静态工厂方法:
	 
	class Child{
		int age;
		int weight;
		public static Child newChild(int age, int weight){
			Child c = new Child();
			c.age = age;
			c.weight = weight;
			return c; 
		}
		public static Child newChildWithAge(int age){
			Child c = new Child();
			c.age = age;
			return c;
		}
		public static Child newChildWithWeight(int weight){
			Child c = new Child();
			c.weight = weight;
			return c;
		}
	}
7. 静态工厂方法能减少对外的暴露

	比如类Player:
		class Player{
			public static int NUM_SWIMMER = 1;
			public static int NUM_RACER = 2;
			public static itn NUM_RUNNER = 3;
			protected int type;

			public Player(int type){
				this.type = type;
			}
		}

	此类若只想让调用者传入的参数只在1,2,3里面选显然不可能

	如果用静态工厂方法将构造方法让外面看不见:

		class Player{
			public static int NUM_SWIMMER = 1;
			public static int NUM_RACER = 2;
			public static itn NUM_RUNNER = 3;
			protected int type;

			private Player(int type){
				this.type = type;
			}

			public static Player newRunner(){
				return new Player(NUM_RUNNER);
			}
			...
		}

	静态方法将构造器隐藏起来只提供给静态方法使用,而外部需要创建Player的时候只能使用静态构造方法
6. 静态变量和实例变量:
1. 静态变量能直接使用类名来使用,而动态变量必须要先实例
2. 静态变量加static
3. 静态变量储存在方法区,属于类所有。实例变量则储存在堆当中
7. 反射机制:
1. 获取Class对象的三种反射:

	1. Class class1 = Class.forName("com.reflection.User");
	2. Class class2 = User.class;
	3. User user = new User();
	   Class class3 = user.getClass();

2. 获取对象实例的两种方法:

	1. user1 = (User)class1.newInstance();
	   user1.setName("a");
	   user1.setAge("15");

	2. Constructor constructor = class2.getConstructor(String.class, Integer.class);
	   user2 = (User)constructor.newInstance("b", 11);
8. 创建对象的方式:
1. new:高耦合
	先查看new后面的类型,再决定分配多大的内存空间
	调用构造函数,填充对象的各个域
	构造方法返回后一个对象创建完毕,把他的引用地址发布到外部
2. 反射机制
3. clone(不会调用构造函数):不是复制地址,而是创建新的对象
	分配内存
	使用原对象的各个域来填充新对象的各个域
4. 使用反序列化(不会调用构造方法)
	所谓序列化就是把对象通过流的方式存储到文件里面(此对象必须先重写Serializable接口)
	那么反序列化也就是把字节内容读出来并还原成java对象,这里还原的过程就是反序列化

	ObjectOutputStream obji = new ObjectOutputStream(new FileOutputStream("Object1.txt"));
    Teacher teacher = new Teacher();
    teacher.setName("张三");
    teacher.setAge(20);

    obji.writeObject(teacher);

    //此时序列化结束,把新建的teacher对象放入了Object1.txt中

    ObjectInputStream Obji = new ObjectInputStream(new FileInputStream("Object1.txt"));
    Teacher t =(Teacher) Obji.readObject();
    System.out.println(t.getName()+t.getAge());

    //此时反序列化结束,此处的t即为通过反序列化建立的对象
9. a=a+b和a+=b的区别
a=a+b不会强制转换类型
a+=b回自动转换类型
例如:
	byte a = 127;
	byte b = 127;
	b = a+b; 		//error: cannot convert from int to byte
	b += a;			//OK


	short s1 = 1;
	s1 = s1+1;		//error: short类型变量在计算的时候会把类型转换为int
	s1 += 1; 		//OK
10. &和&&的区别:
1. &既可以是位运算符也可以是逻辑运算符; &&是逻辑运算符
2. &&有短路特性
11. final, finalize, finally的不同之处:
1. final是一个修饰符,可以修饰变量,方法和类。final修饰意味着在初始化之后就不能再修改了。
2. finalize是在对象被回收之前调用的方法,给对象最后一个复活的机会。
3. finally是与try和catch一起使用的处理异常的方法。
12. GC的流程:
首先要知道的是GC只能对堆区内的进行清理,而对于永久带不能进行GC
  1. JVM堆内存的结构

     Java的堆内存分为:Permanent Space和Heap Space,GC主要针对的是Heap Space
     Heap Space又分为New Generation Area和 Old Generation Area
     在年轻代使用的是Minor GC, 在老年代使用的是Full GC
     在年轻代又分为Eden, Survivor(form), Survivor(to)
    

堆内存的结构图

  1. GC的具体流程

     1. 新对象产生需要一定内存,执行内存空间的申请
     2. 首先判断Eden区是否有内存空间,若此时Eden区空间充足则直接将对象放入Eden区
     3. 若Eden区空间不够用,则自动执行Minor GC来释放空间,将Eden区的垃圾清理掉。这时候再判断Eden区空间够不够,如果够则直接再Eden区进行空间分配。
     4. 若Minor GC后的Eden区空间依然不足,则堆Survivor区进行判断,如果Survivor区空间足够则将Eden区域的活跃对象放入Survivor区域,然后在判断Eden区是否充足。如果Eden区空间充足就进行空间分配。
     5. 如果此时Survivor区也没有空间了,则区判断老年区空间是否足够,如果足够则将Survivor区的活跃对象放入老年区,那么Survivor区就有空间了,然后Eden区就有空间了,然后就可以在Eden区创建对象了。
     6. 若老年区的空间不足了,则堆老年代执行Major GC(Full GC)。若GC后老年区空间够了,就把Survivor的活跃对象移到老年代,然后Survivor空间就够了,然后Eden的空间也够了,然后就能在Eden区建立新的对象了。
     7. 若经过Full GC之后老年代空间依然不足,则此时抛出OOM异常
    

    创建对象的流程

  2. GC与finalize方法

    1. GC中finalize的执行流程:

       1. fianliaze方法是在对象被GC回收了的时候, 且此对象重写了finalize方法,则系统给它的一个复活的机会
       2. 当一个对象在被判断为不可达的时候(可达性分析),GC会判读该对象是否重写了fianlize方法,若没有则直接回收。
       3. 若该对象重写了finalize方法,且没有执行过finalize方法则将其放入F-Queue队列。
       4. 让一个低优先级的线程去执行finalize方法
       5. 执行finalize方法之后再去进行可达性判断,如果还是不可达就清理掉。否则进行复活。
      
    2. GC中finalize的执行流程:

      1. 基础状态说明:

        终结状态空间:

         unfinalized:新建的对象会先进入此状态,GC并未准备清理该对象,也自然不会执行fianlize方法,此时对象可达
         finalizable:标识GC可以对该对象执行finalize方法。此时GC已经检测到了此对象不可达。
         finalized:标识GC已经对该对象执行过了finalize方法
        

        可达性状态空间:

         reachable: 表示GC Roots引用可达
         finalize-reachable:表示不是reachable但是通过某个finalize之后可达
         unreachable:表示对象肯定不可达了
        
      2. finalize执行时状态变迁过程:

         1. 对象刚建立:reachable , unfinalized
         2. 程序运行之后:f-reachable或unreachable
         3. JVM检测到这个对象已经不可达了,则将他的终结状态改为finalizable,表示可以执行finalize方法,再不执行就要被回收了
         4. 在某个时刻,JVM拿出finalizable对象,将其标记为finalized并执行finalize方法。由于在活动线程中引用了该对象,所以该对象的状态变成了reachable, finalized。该动作将影响某些其他对象的状态也变成reachable
         5. 若JVM检测到finalized状态的对象变成unreachable则可以直接将其回收
         6. 若对象未覆盖finalize方法JVM也可以直接将其回收
        
13. 拷贝(引用拷贝&对象拷贝,深拷贝&浅拷贝):

引自 https://blog.csdn.net/baiye_xing/article/details/71788741

1.引用拷贝:

	Teacher teacher = new Teacher("Taylor",26);
	Teacher otherteacher = teacher;
	System.out.println(teacher);
	System.out.println(otherteacher);	//此处的两个输出是一样的(地址)

在这里插入图片描述

  1. 对象拷贝

    Teacher teacher = new Teacher("Swift",26);
    Teacher otherteacher = (Teacher)teacher.clone();
    System.out.println(teacher);
    System.out.println(otherteacher);		//此处输出的结果不同(地址)
    

    在这里插入图片描述
    2.1 浅拷贝(也是对象拷贝):不会复制它引用的对象

    public class ShallowCopy {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher = new Teacher();
            teacher.setName("Delacey");
            teacher.setAge(29);
    
            Student2 student1 = new Student2();
            student1.setName("Dream");
            student1.setAge(18);
            student1.setTeacher(teacher);
    
            Student2 student2 = (Student2) student1.clone();
            System.out.println("拷贝后");
            System.out.println(student2.getName());
            System.out.println(student2.getAge());
            System.out.println(student2.getTeacher().getName());
            System.out.println(student2.getTeacher().getAge());
            System.out.println("修改老师的信息后-------------");
    
            // 修改老师的信息
            teacher.setName("Jam");
            System.out.println(student1.getTeacher().getName());
            System.out.println(student2.getTeacher().getName());
        }
    
    }
    
    class Teacher implements Cloneable
    {
        private String name;
        private int age;
    
        public String getName()
        {
            return name;
        }
    
        public void setName(String name)
        {
            this.name = name;
        }
    
        public int getAge()
        {
            return age;
        }
    
        public void setAge(int age)
        {
            this.age = age;
        }
    
    }
    
    class Student2 implements Cloneable
    {
        private String name;
        private int age;
        private Teacher teacher;
    
        public String getName()
        {
            return name;
        }
    
        public void setName(String name)
        {
            this.name = name;
        }
    
        public int getAge()
        {
            return age;
        }
    
        public void setAge(int age)
        {
            this.age = age;
        }
    
        public Teacher getTeacher()
        {
            return teacher;
        }
    
        public void setTeacher(Teacher teacher)
        {
            this.teacher = teacher;
        }
    
        @Override
        public Object clone() throws CloneNotSupportedException
        {
            Object object = super.clone();
            return object;
        }
    
    }
    

    在这里插入图片描述
    此时两个student所引用的teacher是一个,所以对teacher进行修改的时候会对两个student都产生影响。

    2.2 深拷贝

    public class DeepCopy {
        public static void main(String[] args) throws Exception
        {
            Teacher2 teacher = new Teacher2();
            teacher.setName("Delacey");
            teacher.setAge(29);
    
            Student3 student1 = new Student3();
            student1.setName("Dream");
            student1.setAge(18);
            student1.setTeacher(teacher);
    
            Student3 student2 = (Student3) student1.clone();
            System.out.println("拷贝后");
            System.out.println(student2.getName());
            System.out.println(student2.getAge());
            System.out.println(student2.getTeacher().getName());
            System.out.println(student2.getTeacher().getAge());
            System.out.println("修改老师的信息后-------------");
    
            // 修改老师的信息
            teacher.setName("Jam");
            System.out.println(student1.getTeacher().getName());
            System.out.println(student2.getTeacher().getName());
        }
    }
    
    class Teacher2 implements Cloneable {
        private String name;
        private int age;
    
        public String getName()
        {
            return name;
        }
    
        public void setName(String name)
        {
            this.name = name;
        }
    
        public int getAge()
        {
            return age;
        }
    
        public void setAge(int age)
        {
            this.age = age;
        }
    
        @Override
        public Object clone() throws CloneNotSupportedException
        {
            return super.clone();
        }
    
    }
    
    class Student3 implements Cloneable {
        private String name;
        private int age;
        private Teacher2 teacher;
    
        public String getName()
        {
            return name;
        }
    
        public void setName(String name)
        {
            this.name = name;
        }
    
        public int getAge()
        {
            return age;
        }
    
        public void setAge(int age)
        {
            this.age = age;
        }
    
        public Teacher2 getTeacher()
        {
            return teacher;
        }
    
        public void setTeacher(Teacher2 teacher)
        {
            this.teacher = teacher;
        }
    
        @Override
        public Object clone() throws CloneNotSupportedException
        {
            // 浅复制时:
            // Object object = super.clone();
            // return object;
    
            // 改为深复制:
            Student3 student = (Student3) super.clone();
            // 本来是浅复制,现在将Teacher对象复制一份并重新set进来
            student.setTeacher((Teacher2) student.getTeacher().clone());
            return student;
        }
    

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Wenbiiiin/article/details/88173559