【面试】【面试题】

1.JAVA 面试题

https://blog.csdn.net/linzhiqiang0316/article/details/80473906

https://blog.csdn.net/fanx_000/article/details/80297487

https://blog.csdn.net/hope900/article/details/78647466/

 

2.JD 面试

https://www.nowcoder.com/discuss/91413

 

JAVA

1.面向对象的三个特征

封装,继承,多态

多态

多态指允许不同类的对象对同一“消息”做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。可以用于消除类型之间的耦合关系。

实现多态主要有以下三种方式:
1. 接口实现 
2. 继承父类重写方法 
3. 同一类中进行方法重载

 

2.java 创建对象的几种方式

https://www.cnblogs.com/wxd0108/p/5685817.html

1.new:

   创建一个新类
   强类型,相对高效。能调用任何public构造

2.newInstance:

   类加载机制
  1.先调用Class加载方法 加载某个类;

  2.然后把加载的类实例化 。

  即所谓的反射机制
  通过反射创建对象 ,弱类型。低效率。只能调用无参构造:

   https://www.cnblogs.com/liuyanmin/p/5146557.html

             1.虚拟机里面这个类已经加载;

             2.这个类已经连接了

 // 初始化 Context 类的对象
 ctor = wigigClass.getConstructor(Context.class);
 // 实例化这个对象
 wigigService = ctor.newInstance(context);
Method method =Class.forName("Android.os.ServiceManager") .getMethod("getService",String.class);

// 获取远程TELEPHONY_SERVICE的IBinder对象的代理

IBinder binder =(IBinder) method.invoke(null, new Object[] { TELEPHONY_SERVICE});

// 将IBinder对象的代理转换为ITelephony对象

ITelephonytelephony = ITelephony.Stub.asInterface(binder);

// 挂断电话

telephony.endCall();

Binder 通信里面就是newInstance 的

3.clone

jvm就会创建一个新的对象,将前面对象的内容全部拷贝进去。用clone方法创建对象并不会调用任何构造函数。

4.使用反序列化

当我们序列化和反序列化一个对象,jvm会给我们创建一个单独的对象。在反序列化时,jvm创建对象并不会调用任何构造函数。
为了反序列化一个对象,我们需要让我们的类实现Serializable接口

例子
 

public class ObjectCreation {
    public static void main(String... args) throws Exception {
        // 1 new 创建
        Employee emp1 = new Employee();
        emp1.setName("Naresh");


        // 2  Class class类的newInstance() 方法
        Employee emp2 = (Employee)Class.forName("org.programming.mitra.exercises.Employee")
.newInstance();
        // 或者
        // Employee emp2 = Employee.class.newInstance();
        emp2.setName("Rishi");


        // 3  Constructor class类的 newInstance() 方法
        Constructor<Employee> constructor = Employee.class.getConstructor();
        Employee emp3 = constructor.newInstance();
        emp3.setName("Yogesh");


        // 4 clone() 方法
        Employee emp4 = (Employee) emp3.clone();
        emp4.setName("Atul");


        // 5 Serialization
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
        out.writeObject(emp4);
        out.close();

        //Deserialization
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
        Employee emp5 = (Employee) in.readObject();
        in.close();
        emp5.setName("Akash");
        System.out.println(emp5 + ", hashcode : " + emp5.hashCode());
    }
}

3.static , final , static final 的区别

1.static

static修饰的类成员,独立于该类的任何实例化的对象,在程序运行过程中,其内存仅被初始化一次,但被类的所有实例共享。

    1.不加static修饰的成员是对象成员,归每个对象所有。

    2.加static修饰的成员是类成员,可以由一个类直接调用,为所有对象共有。

用static关键字标识的程序元素是静态的,在此类被加载时初始化。

static 方法:在不创建此类的对象的情况下调用某个方法,就可以将这个方法设置为static,静态方法里只能调用静态变量。

2. final

1.用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以改变它的值。

   1. 修饰数据:数据无法被修改;

   2. 修饰方法:方法无法被覆盖,但是可以被继承;

  3.  修饰类 :  类无法被继承;

final有哪些用法


1.被final修饰的类不可以被继承 
2.被final修饰的方法不可以被重写 
3.被final修饰的变量不可以被改变。如果修饰引用,那么表示引用不可变,引用指向的内容可变。
4.被final修饰的方法,JVM会尝试将其内联,以提高运行效率 
5.被final修饰的常量,在编译阶段会存入常量池中。

回答出编译器对final域要遵守的两个重排序规则更好:
1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

3.static final

同时使用static和final修饰的成员在内存中只占据一段不能改变的存储空间

4. public static final

public(类的子类也可以访问) static (类的静态成员,类的所有子类都可以访问,无需初始化)final (无法被修改)

 

4. JAVA 创建线程的几种方法

https://www.cnblogs.com/3s540/p/7172146.html

1.继承Thread 类创建线程

    1.定义Thread类的子类并重写该类的run() 方法, run()的方法体就是线程需要完成的任务;

    2.创建Thread子类的实例,也就是创建线程对象;

    3.启动线程,调用线程是start() 方法;

2.实现Runable接口创建线程

    1.定义Runable接口的实现类,重写run() 方法,run()的方法体就是线程需要完成的任务;

    2.创建Runable 实现类的实例,并用这个实例作为Thread 的target来创建Thread对象,这个Thread对象才是真正的线程对象;

    3.调用线程对象的start()方法来启动线程;

//实现Runnable接口
public class MyThread2 implements Runnable {

  public void run(){

  //重写run方法

  }

}

public class Main {

  public static void main(String[] args){

    //创建线程实例
    MyThread2 myThread=new MyThread2();

       //实例作为Thread 的target,初始化thread 对象
    Thread thread=new Thread(myThread);

       //启动thread
    thread().start();

  }

}

 

*4.x* 继承Thread 和实现Runable 的区别

 一般多线程环境下都是用 实现Runable 的方式来创建线程,利用Runable启动线程中,只new 了一个实现Runable 接口的实例,多线程中,传入Thread 的都是这个相同的实例,达到多线程下的数据共享

package multithreading;  
  
public class MyThreadWithImplements implements Runnable {  
  
    private int tickets = 10;  
  
    @Override  
    public void run() {  
  
        for (int i = 0; i <= 100; i++) {  
            if(tickets>0){  
                System.out.println(Thread.currentThread().getName()+"--卖出票:" + tickets--);  
            }  
        }  
    }  
      
      
    public static void main(String[] args) {  
        MyThreadWithImplements myRunnable = new MyThreadWithImplements();  
        Thread thread1 = new Thread(myRunnable, "窗口一");  
        Thread thread2 = new Thread(myRunnable, "窗口二");  
        Thread thread3 = new Thread(myRunnable, "窗口三");  
  
        thread1.start();  
        thread2.start();  
        thread3.start();  
    }  
  
}  

运行结果:

窗口二--卖出票:10
窗口三--卖出票:9
窗口一--卖出票:8
窗口三--卖出票:6
窗口三--卖出票:4
窗口三--卖出票:3
窗口三--卖出票:2
窗口三--卖出票:1
窗口二--卖出票:7
窗口一--卖出票:5

3.使用Callable和Future创建线程

 和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大

1.创建callable接口的实现类,并实现call() 方法,然后创建该类的实例;

2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call() 方法的返回值;

3.使用FutrueTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runable接口);

4.调用FutureTask对象的get()方法来获得子线程执行结束的返回值;

 

5. JAVA 反射机制

https://blog.csdn.net/sinat_38259539/article/details/71799078

PYJ: 虚拟机加载磁盘中每个XXX.class 文件时,都会在JVM 里生成一个class 对象,反射就是通过获取到此class 对象而反向的获取对于的构造方法和参数相关。

运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制

Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了 Field,Method,Constructor 类 (每个类都实现了 Member 接口)。这些类型的对象时由 JVM 在运行时创建的,用以表示未知类里对应的成员

1.获取字节码(class 对象)

    (方法一)通过 Object 类中的 getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象。

    (方法二)所有数据类型都具备一个静态的属性,通过XXX.class 来获取对应的 Class 对象。但是还是要明确到类,然后才能调用类中的静态成员????

     (方法三)只要通过给定类的字符串名称就可以获取该类的字节码对象,这样做扩展性更强。通过 Class.forName() 方法完成,必须要指定类的全限定名

package fanshe;
/**
 * 获取Class对象的三种方式
 * 1 Object ——> getClass();
 * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
 * 3 通过Class类的静态方法:forName(String  className)(常用)
 *
 */
public class Fanshe {
	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();
		}
		
	}
}

所有类都继承Object类,Object.getClass()就是获取此类的class 对象;

在运行期间,一个类,只有一个Class对象产生。

2.通过反射获取构造方法

通过class 类的getConstructor 方法等获取构造函数

//获取到此类的class 对象
Class clazz = Class.forName("fanshe.Student");

//调用class.getConstructor 获取无参构造方法
Constructor con = clazz.getConstructor(null);

//有参
con = clazz.getDeclaredConstructor(char.class)

3.例子

//1.获取类的class对象

Class stuClass = Class.forName("fanshe.field.Student");

//2.stuClass.getConstructor -》获取类的构造方法 , stuClass.getConstructor.newInstance生成student对应的Object 对象

Object obj = stuClass.getConstructor().newInstance();

//3.初始化student对象

Student stu = (Student)obj;

6.类的“替换”

Q: 怎么在不修改A.class 文件的前提下,修改调用A 时 函数的输出?

A: 创建一个B extends A ,在B.class 文件中复写A.class 中定义的函数,在a =new A()时 ,改成 a = new B() , 利用JAVA 的多态特性,后续 对 a 的操作,实际都是操作B 类

 

7.父类的静态方法能否被子类重写

不可以。静态方法是类被虚拟机 加载/运行 时就分配了存储空间保存好了,子类只能调用,不能重写

8.静态变量和实例变量的区别?

静态变量存储在方法区,属于类所有。

实例变量存储在堆当中,其引用存在当前线程栈

9.能否创建一个包含可变对象的不可变对象?

????

10.switch中能否使用string做参数

可以。idk 1.7之后switch开始支持String

11. “==” 和 “equals”

https://blog.csdn.net/qauchangqingwei/article/details/80831797

1. n =3;
   m =3;
   n == m ---> true;

2. str = "hello";
   str2 = "hello";
   str == str2 ---> true 
// str 和str2 都指向同一字符常量“hello”的引用地址
// JAVA在编译 str = "hello" 时,将“hello”放在常量池中,编译“str2 = “hello” ” 时,会先检查“hello”这个字符是否已经在常量池中,如果有,则直接把str2 指向之前创建的“hello”的常量池;

3. String str3=new String("hello");
   String str4=new String("hello");
   str3==str4 ---> false 
//"new" 关键字代表创建新的对象,每次调用,都会先创建新的对象,在对其进行赋值初始化
//-------------important--------------------------------------------
每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串
----------------------------------------------------------------------//

4. str.equals(str2) --->true


5. String str1="abc";
   String str2="def";
   String str3=str1+str2;
   str3=="abcdef" --->false
   //str3 指向堆中是“abcdef”,但是是在运行时才确定的,== 右边的“abcdef”是编译时已经放在常量池的一个值

   1. “== ” 用来比较值是否相等

      1. JAVA 中的8种基本数据类型 浮点型:float(4 byte), double(8 byte) , 整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte) , 字符型: char(2 byte) , 布尔型: boolean 变量直接存储的是“值”

      2. 非基本数据类型时,就是引用类型变量,变量存储的并不是 “值”本身,而是于其关联的对象在内存中的地址

    2. equals

       equal 是超类Object 的方法, string对equals 进行了复写,用于比较指向对象所存储的内容是否相等

 

12. string 和 char

   C++ : 

   char 是基本数据类型 , 字符;

   string 是封装好的类,有一些成员函数比如begin()、end();

   char* , 一个指针,可以指向一个字符数组;

   1 . char *s="string" :指向字符常量“string”的指针s,内容是不可以改的 ;

   2 . char s[10]="string":存放字符的数组s , 的内容是可以改的;

   3.  string s = "string" : string 类型的对象;

   4.  char* a[] : 指针类型数组,存放的成员是指针;

13. 内部类

  定义在一个类 或者方法里面的类:成员内部类、局部内部类、匿名内部类和静态内部类:

1.成员内部类:

    1  . 创建成员内部类的对象,前提是必须存在一个外部类的对象 ;

    2  外部类.this.成员变量  , 外部类.this。成员方法;

 3. 如果成员内部类用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。

    4. 匿名内部类:

         
        history_bt.setOnClickListener(new OnClickListener() {
             
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                 
            }
        }

OnClickListener 就是一个匿名内部类,没有构造函数,不能有访问修饰符和static 修饰符。

内部类可以继承一个与外部类无关的类,保证了内部类的独立性

14  .java中int char,long各占多少字节?

类型 位数 字节数
short 2 16
int 4 32
long 8 64
float 4 32
double 8 64
char 2 16

 15  . String,StringBuffer,StringBulilder

    1.string 是字符常量,字符不可变,StringBuffer 和StringBuilder是字符串变量,字符可变

    2.StringBuilder是后出来的,无锁,是线程不安全的,而StringBuffer有锁,是线程安全的

 16. 多线程 相关

https://www.wengbi.com/thread_84044_1.html

   1、多线程有什么用?

    1)发挥多核CPU的优势 , 2)防止阻塞

   2、创建线程的方式

   1)继承Thread类  , 2)实现Runnable接口

  3、start()方法和run()方法的区别

    run() 是线程运行时执行的逻辑

    start () 是线程启动的时间点

   4.Runnable接口和Callable接口的区别

    1)Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;

    2)Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果;

    7、什么是线程安全

    如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。

    8、Java中如何获取到线程dump文件

    死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:

   (1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java

   (2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid

  9. 多线程共享数据的方式: 

   1. 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据;

   2. 如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,即

       1)将共享数据封装成另外一个对象,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上完成,这样容易实现针对数据进行各个操作的互斥和通信;

       2)将Runnable对象作为一个类的内部类,共享数据作为这个类的成员变量,每个线程对共享数据的操作方法也封装在外部类,以便实现对数据的各个操作的同步和互斥,作为内部类的各个Runnable对象调用外部类的这些方法。                                           3)多个线程对共享数据的操作是不同的,将共享数据和操作共享数据的方法放在同一对象中,将这个对象作为参数传递给Runnable的子类,在子类中用该对象的方法对共享数据进行操作

   10. sleep() 和 wait() 的区别

    1) sleep()属于Thread 类,程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。sleep时线程不会释放对象锁

    2) wait()方法属于Object类, 线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。

  11. 垃圾回收 

    根据对象存活的时间划分成3个世代:年轻、年老和永久。当一个对象存活时间足够长的时候,它就会被复制到年老世代中,对于不同的世代可以使用不同的垃圾回收算法

    调用System.gc() 进行垃圾回收

 

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章