面试——Java

  1. 面向对象的特征有哪些方面?
    抽象:抽象是将一类对象的共同特征总结出来构造类的过程。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
    继承:子类继承父类的属性和方法。从而提高代码复用性、扩展性。使类与类之间产生了关系,有了这个关系,才有多态的特性。
    多态:父类型的引用指向子类型的对象。同一操作作用于不同的对象,可以产生不同的效果。 Java引用变量有两个类型:编译时类型,运行时类型。编译时类型由声明该变量时使用的类型决定。运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现多态。
    多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法, 不可以访问子类中特有的方法。
    封装:将属性私有化,提供公有的方法访问私有属性。通过封装,可以实现对属性的数据访问限制,同时增加了程序的可维护性。

  2. int和Integer有什么区别?
    Java为了编程的方便还是引入了基本数据类型,但为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型,int的包装类就是Integer。从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。装箱就是自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型。
    装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf。简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象。

  3. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
    重载:在一个类定义中,可以编写几个同名的方法,但是它们的参数列表不同。

重写:即子类把父类的方法覆盖了,重新实现;并且要符合里氏代换原则;重写要求子类被重写方法与父类被重写方法有相同的返回类型。重载对返回类型没有特殊的要求。

  1. 抽象类(abstract class)和接口(interface)有什么异同?
    抽象类:包含抽象方法的类就是抽象类
    接口:指抽象方法的集合,方法不包含方法体
    相同点:
    都不能被实例化
    接口的实现类或者抽象类的子类都必须实现了接口或抽象类中的方法后才可以被实例化
    不同点:
    抽象类可以有构造方法,抽象方法和具体方法。接口不能有构造方法,而且其中的方法全部都默认是抽象方法。
    抽象类中的成员可以使private、默认、protected、public的。接口中的成员全部都是public的。
    接口要实现,抽象类要继承,一个类可以实现多个接口,但只能继承一个抽象类。
    接口强调设计理念为“has -a”的关系,抽象类强调“is -a”关系。
    接口被运用于比较常用的功能,便于日后的维护或者添加删除方法;而抽象类更倾向于充当公共类的角色,不适用于对里面的代码进行修改。
    场景:
    接口是一种特殊形式的抽象类,使用接口完全有可能实现与抽象类相同的操作。当子类和父类之间存在有逻辑上的层次结构时,推荐使用抽象类;当用于不同类之间,希望支持差别较大的两个或者更多对象之间的特定交互行为时,应该使用接口。

  2. GC是什么?为什么要有GC?
    GC是垃圾收集的意思,垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。

  3. 接口是否可继承接口?抽象类是否可实现接口?抽象类是否可继承具体类?
    接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。

  4. Java 中堆和栈有什么区别?
    JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

  5. **JDK和JRE的区别是什么? **
    JRE是java运行时环境,包含了java虚拟机,java基础类库。是提供给想运行java程序的用户使用的。
    JDK顾名思义是java开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具。

  6. CLASSPATH / PATH 是什么?它的作用是什么?
    CLASSPATH是 javac 编译器的一个环境变量。它的作用与 import、package 关键字有关。PATH 环境变量。作用是指定命令搜索路径。


  7. 主函数:保证所在类的独立运行,是程序的入口,被 jvm 调用。
    成员变量和局部变量的区别:成员变量定义在类中。在这个类中有效。存在于堆内存中,随着对象产生和消失。 局部变量定义在方法、参数上、语句中。只在自己所属的大括号内有效。存在于栈内存中。
    构造函数:用于给对象进行初始化,名称和所在类的名称相同。 不需要定义返回值。所有对象创建时,都需要初始化才可以使用。
    Person p = new Person(); 创建一个对象都在内存中做了什么事情? 1:先将硬盘上指定位置的 Person.class 文件加载进内存。 2:执行 main 方法时,在栈内存中开辟了 main 方法的空间(压栈-进栈),然后在 main 方法的 栈区分配了一个变量 p。 3:在堆内存中开辟一个实体空间,分配了一个内存首地址值。new 4:在该实体空间中进行属性的空间分配,并进行了默认初始化。 5:对空间中的属性进行显示初始化。 6:进行实体的构造代码块初始化。 7:调用该实体对应的构造函数,进行构造函数初始化。() 8:将首地址赋值给 p ,p 变量就引用了该实体。(指向了该对象)
    this:代表对象。就是所在函数所属对象的引用。

  8. static 关键字:是一个修饰符,用于修饰成员变量和成员函数。
    静态变量:JVM 只为静态变量分配一次内存,在加载类的过程中完成,可用类名直接访问。 实例变量:每创建一个实例,就会为实例变量分配一次内存。
    静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用 this 和 super 关键字。
    静态代码块:是在类中独立于类成员的 ,可以有多个,位置可以随便放,它不在任何的方法体内。
    static final 用来修饰成员变量和成员方法,可简单理解为“全局常量”。 对于变量,表示一旦给值就不可修改。对于方法,表示不可覆盖。
    静态方法只能访问静态成员,不可以访问非静态成员。
    静态方法中不能使用 this,super 关键字。
    成员变量和静态变量的区别: 成员变量所属于对象。存在于堆内存中。随着对象创建存在和消失。 只能被对象所调用
    静态变量所属于类。存在于方法区中。随着类的加载存在和消失。 可以被对象调用,也可以被类名调用。
    静态代码块:就是一个有静态关键字标示的一个代码块区域。定义在类中。 作用:可以完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次。如果和主函数在同一类中,优先于主函数执行。

  9. 继承
    成员变量:当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。 如果想要调用父类中的属性值,需要使用一个关键字:super
    成员函数。 当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。即重写。
    构造函数。 发现子类构造函数运行时,先运行了父类的构造函数。为什么呢? 原因:子类的所有构造函数中的第一行,其实都有一条隐身的语句 super();子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构 造内第一行都有默认的语句 super()
    super()和 this()是否可以同时出现的构造函数中? 两个语句只能有一个定义在第一行,所以只能出现其中一个。 super()或者this():为什么一定要定义在第一行? 因为 super()或者 this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完 成。
    final 特点: 1:这个关键字是一个修饰符,可以修饰类,方法,变量。 2:被 final 修饰的类是一个最终类,不可以被继承。 3:被 final 修饰的方法是一个最终方法,不可以被覆盖。 4:被 final 修饰的变量是一个常量,只能赋值一次。

  10. 内部类
    如果 A 类需要直接访问 B 类中的成员,而 B 类又需要建立 A 类的对象。这时,为了方便设 计和访问,直接将 A 类定义在 B 类中。就可以了。A 类就称为内部类。内部类可以直接访问外部类 中的成员。而外部类想要访问内部类,必须要建立内部类的对象。
    内部类直接访问外部类成员,用自己的实例对象;外部类访问内部类要定义内部类的对象。
    匿名内部类(对象):没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形 式。匿名内部类其实就是一个匿名子类对象。想要定义匿名内部类:需要前提,内部类必须继承 一个类或者实现接口。

  11. 异 常
    throw 与 throws 区别: throws 是用来声明一个方法可能抛出的所有异常信息,throws 是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。 后面跟的异常类名。用在函数上。而 throw 则是指抛出的一个具体的异常类型。用于抛出异常对象,后面跟的是异常对象;throw 用在函数内。
    定义异常处理时,什么时候定义 try,什么时候定义 throws 呢? 功能内部如果出现异常,如果内部可以处理,就用 try; 如果功能内部处理不了,就必须声明出来,让调用者处理。使用 throws 抛出,交给调用者处理。 谁调用了这个功能谁就是调用者;
    如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能 try 不能 throws。

  12. 多线程
    线程要运行的代码都统一存放在了 run 方法中。
    线程要运行必须要通过类中start 方法开启。
    Thread 类中 run()和 start()方法的区别: :start()方法最本质的功能是从 CPU 中申请另一个线程空间来执行 run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行,也就是说,如果你直接调用线程对象的 run()方法,当然也会执行,但那是 在当前线程中执行,run()方法执行完成后继续执行下面的代码. 而调用 start()方法后,run()方法的代码会和当前线程并发(单 CPU)或并行 (多 CPU)执行。所以请 记住一句话:调用线程对象的 run 方法不会产生一个新的线程,虽然可以达到相同的执行结果,但 执行过程和执行效率不同
    创建线程的方式:1 :继承 Thread ,由子类复写 run 方法。2 实现一个接口 Runnable。
    为什么要有 Runnable 接口的出现? 1:因为实现Runnable 接口可以避免单继承的局限性。 2:所以 Runnable 接口将线程要执行的任务封装成了对象。

  13. synchronized 关键字

synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方 法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁, 直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机 制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处 于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访 问冲突。

synchronized 方法的缺陷:若将一个大的方法声明为 synchronized 将会大大影响效率,典型 地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行, 因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类 成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问 题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可 以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上 锁的对象,故灵活性较高。

一、当两个并发线程访问同一个对象 object 中的这个 synchronized(this)同步代码块时,一 个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行 该代码块。 二、然而,当一个线程访问 object 的一个 synchronized(this)同步代码块时,另一个线程仍 然可以访问该 object 中的非 synchronized(this)同步代码块。 三、尤其关键的是,当一个线程访问 object 的一个 synchronized(this)同步代码块时,其他 线程对 object 中所有其它 synchronized(this)同步代码块的访问将被阻塞。 四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问 object 的一个 synchronized(this)同步代码块时,它就获得了这个 object 的对象锁。结果,其它线程对该 object 对象所有同步代码部分的访问都被暂时阻塞。 五、以上规则对其它对象锁同样适用.

解决安全问题的原理: 只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执 行就可以解决这个问题。 如何保障共享数据的线程安全呢? java中提供了一个解决方式:就是同步代码块。

同步好处:解决了线程安全问题。Synchronized 弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁。

同步函数是用的哪个锁呢? //synchronized(this)用以定义需要进行同步的某一部分代码块 通过验证,函数都有自己所属的对象 this,所以同步函数所使用的锁就是 this 锁。

同步代码块和同步函数的区别? 同步代码块使用的锁可以是任意对象。 同步函数使用的锁是 this,静态同步函数的锁是该类的字节码文件对象。 在一个类中只有一个同步的话,可以使用同步函数。如果有多同步,必须使用同步代码块, 来确定不同的锁。所以同步代码块相对灵活一些。

wait和 sleep 区别: wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的 notify 或者 notifyAll 来唤醒。 线程会释放执行权,而且线程会释放锁。sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。 线程会释放执行权,但不是不释放锁。

Lock接口:多线程在 JDK1.5 版本升级时,推出一个接口 Lock 接口。 解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。 同步是隐示的锁操作,而 Lock 对象是显示的锁操作。

  1. **集合框架 **
    List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。 |–ArrayList:底层的数据结构是数组,线程不同步,ArrayList 替代了 Vector,查询元素的 速度非常快。 |–LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。 |–Vector:底层的数据结构就是数组,线程同步的,Vector 无论查询和增删都巨慢。
可变长度数组的原理: 当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到 新数组中。 ArrayList:是按照原数组的 50%延长。构造一个初始容量为 10 的空列表。 Vector:是按照原数组的 100%延长。

Set 接口中的方法和 Collection 中方法一致的。Set 接口取出方式只有一种,迭代器。  |--HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效;   HashSet 集合保证元素唯一性:通过元素的 hashCode 方法,和 equals 方法完成的。   当元素的 hashCode 值相同时,才继续判断元素的 equals 是否为 true。   如果为 true,那么视为相同元素,不存。如果为 false,那么存储。   如果 hashCode 值不同,那么不判断 equals,从而提高对象比较的速度。     

LinkedHashSet:有序,hashset 的子类。  |--TreeSet:对 Set 集合中的元素的进行指定顺序的排序。不同步。TreeSet 底层的数据结构 就是二叉树。 

Map 集合: |--Hashtable:底层是哈希表数据结构,是线程同步的。不可以存储 null 键,null 值。 |--HashMap:底层是哈希表数据结构,是线程不同步的。可以存储 null 键,null 值。替代了 Hashtable. |--TreeMap:底层是二叉树结构,可以对 map 集合中的键进行指定顺序的排序。  

Map 集合存储和 Collection 有着很大不同: Collection 一次存一个元素;Map 一次存一对元素。 Collection 是单列集合;Map 是双列集合。 Map 中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。 特点:要保证 map 集合中键的唯一性。 

想要获取 map 中的所有元素:  原理:map 中是没有迭代器的,collection 具备迭代器,只要将 map 集合转成 Set 集合,可以 使用迭代器了。之所以转成 set,是因为 map 集合具备着键的唯一性,其实 set 集合就来自于 map, set 集合底层其实用的就是 map 的方法。 方式一:keySet()方法。 可以将 map 集合中的键都取出存放到 set 集合中。对 set 集合进行迭代。迭代完成,再通过 get 方法对获取到的键进行值的获取。方式二:entrySet()方法。

将非同步集合转成同步集合的方法:Collections 中的  XXX synchronizedXXX(XXX); 原理:定义一个类,将集合所有的方法加同一把锁后返回。 

Collection 和 Collections 的区别: Collections 是个 java.util 下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对 集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。 Collection 是个 java.util 下的接口,它是各种集合结构的父接口,继承于它的接口主要有 Set 和 List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。 

泛型:jdk1.5 版本以后出现的一个安全机制。表现格式:< > 好处: 1:将运行时期的问题 ClassCastException 问题转换成了编译失败,体现在编译时期,程序员就 可以解决问题。 2:避免了强制转换的麻烦。 泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是 ?  。
  1. **反射技术 **:是动态加载一个指定的类,并获取该类中的所有的内容。并将字节码文件中的 内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。
    好处:大大的增强了程序的扩展性。
    反射的基本步骤: 1、获得Class 对象,就是获取到指定的名称的字节码文件对象。 2、实例化对象,获得类的属性、方法或构造函数。 3、访问属性、调用方法、调用构造函数创建对象。
    获取这个Class 对象:通过每个对象都具备的方法 getClass 来获取。弊端:必须要创建该类对象,才可以调用 getClass 方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值