Java基础知识

23 篇文章 0 订阅
2 篇文章 0 订阅

本文从JavaGuide面试突击版整理得到,侵删。

1. ⾯向对象和⾯向过程的区别

⾯向过程 :
⾯向过程性能⽐⾯向对象⾼。 因为类调⽤时需要实例化,开销⽐较⼤,⽐较消耗资源,所以当性能是最重要的考量因素的时候,⽐如单⽚机、嵌⼊式开发、Linux/Unix 等⼀般采⽤⾯向过程开发。但是,⾯向过程没有⾯向对象易维护、易复⽤、易扩展。
⾯向对象 :
⾯向对象易维护、易复⽤、易扩展。 因为⾯向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,⾯向对象性能⽐⾯向过程低。

2. JDK、JRE 和JVM 的区别?(考察一次)

JDK:
即Java Development Kit,它是功能⻬全的 Java SDK(Software Development Kit 软件开发工具包)。它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。
JRE:
即Java 运⾏时环境。它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的⼀些基础构件。但是,它不能⽤于创建新程序。
JVM:
即Java 虚拟机,是运⾏ Java 字节码(.class文件)的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,mac OS),⽬的是使⽤相同的字节码,它们都会给出相同的结果。

补充:
javadoc是Sun公司提供的一个技术,它可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写完成后,通过Javadoc就可以同时形成程序的开发文档了。javadoc命令是用来生成自己API文档的,使用方式:使用命令行在目标文件所在目录输入javadoc+文件名.java。
jdb:基于文本和命令行的调试工具。

3. Java 语⾔有哪些特点?

  1. 简单易学;
  2. ⾯向对象(封装,继承,多态);
  3. 平台⽆关性( Java 虚拟机实现平台⽆关性);
  4. 可靠性,安全性;
  5. ⽀持多线程;
  6. ⽀持⽹络编程并且很⽅便

4. Java为什么可以实现平台无关性?(考察1次)

Java 程序从源代码到运⾏⼀般有下⾯ 3 步:
在这里插入图片描述
在 Java 中,JVM 可以理解的代码就叫做字节码 (即扩展名为 .class 的⽂件),它不⾯向任何特定的处理器,只⾯向虚拟机。Java 虚拟机(JVM)是运⾏ Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,mac OS),⽬的是使⽤相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语⾔“⼀次编译,随处可以运⾏”的关键所在,使得Java 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏。

5. Java 和 C++的区别?(考察一次)

  1. 都是⾯向对象的语⾔,都⽀持封装、继承和多态;
  2. Java 不提供指针来直接访问内存,程序内存更加安全;
  3. Java 的类是单继承的,C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多继承;
  4. Java 有⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存;
  5. 在 C 语⾔中,字符串或字符数组最后都会有⼀个额外的字符‘\0’来表示结束。但是,Java 语⾔中没有结束符这⼀概念。

6. 什么是 Java 程序的主类?应⽤程序和⼩程序的主类有何不同?

⼀个程序中可以有多个类,但只能有⼀个类是主类。在 Java 应⽤程序中,这个主类是指包含main()⽅法的类。⽽在 Java ⼩程序中,这个主类是⼀个继承⾃系统类 JApplet 或 Applet 的⼦类。

应⽤程序的主类不⼀定要求是 public 类,但⼩程序的主类要求必须是 public 类。主类是 Java程序执⾏的⼊⼝点。

应⽤程序是从主线程启动(也就是 main() ⽅法)。applet ⼩程序没有 main() ⽅法,主要是嵌在浏览器⻚⾯上运⾏(调⽤ init() 或者 run() 来启动)。

7. 字符型常量和字符串常量的区别?

  1. 形式上: 字符常量是单引号引起的⼀个字符; 字符串常量是双引号引起的若⼲个字符。
  2. 含义上: 字符常量相当于⼀个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表⼀个地址值(即该字符串在内存中存放位置)。
  3. 占内存⼤⼩ 字符常量只占 2 个字节; 字符串常量占若⼲个字节 。
    注:char在java中是两个字节,java采用Unicode,2个字节(16位)表示一个字符。

8. 构造器 Constructor 是否可被 override(重写)?

Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。
一旦提供了一个有参的构造方法同时又没有显式的提供一个无参的构造方法,那么默认的无参的构造方法,就没有了。

9. 重载和重写的区别(考察1次)

重载:
同⼀个类中多个同名⽅法根据不同的传参来执⾏不同的逻辑处理。Java允许重载任何方法,而不只是构造器方法。因此要完整的描述一个方法,需要指出方法名以及参数类型,这叫做方法的签名。但是返回类型不是方法签名的一部分。也就是说,同一个类中,不能有两个名字相同,参数类型也相同却返回不同类型的方法。

重写:
⼦类对⽗类的允许访问的⽅法的重新编写。

  1. ⽅法名、参数列表必须相同,返回值范围、抛出的异常范围⼩于等于⽗类,访问修饰符范围⼤于等于⽗类。
  2. 如果⽗类⽅法访问修饰符为 private/final/static,则⼦类就不能重写该⽅法,但是被 static修饰的⽅法能够被再次声明。
  3. 构造⽅法⽆法被重写。

10. Java ⾯向对象编程三⼤特性: 封装 继承 多态

封装: 隐藏对象的属性和实现细节,仅对外提供公共访问方法,提高了代码的复用性和安全性。

继承: 使⽤已存在的类的定义作为基础建⽴新类,新类的定义可以增加新的数据或新的功能,也可以⽤⽗类的功能,但不能选择性地继承⽗类。通过使⽤继承能够⾮常⽅便地复⽤以前的代码。
关于继承如下 3 点请记住:

  1. 从继承的概念来说,private和final修饰的属性和方法不被继承。Java官方文档上是这么说的。从内存的角度来说,父类的一切都被继承,但是父类中的私有属性和方法是子类无法访问的,只是拥有。
  2. ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
  3. ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法,即重写。

多态: 即⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤变量发出的⽅法调⽤到底是哪个类中实现的⽅法,这些都必须在由程序运⾏期间才能决定。
在 Java 中有三种形式可以实现多态:
继承(多个⼦类对同⼀⽅法的重写);接⼝(实现接⼝并覆盖接⼝中同⼀⽅法);抽象类和抽象方法。

11. ⾃动装箱与拆箱

所有的基本类型,都有对应的类类型,比如int对应的类是Integer,这种类就叫做封装类。(数字封装类有Byte,Short,Integer, Long,Float,Double,这些类都是抽象类Number的子类)。

以int和其封装类Integer为例:
自动装箱: 不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫自动装箱。
int i = 5;
Integer it2 = i;
自动拆箱: 不需要调用Integer的intValue方法,通过=就自动转换成int类型,就叫拆箱:
int i3 = it2;

12. int和Integer,Integer的比较问题

  1. Integer是int的包装类,默认值是null,int则是java的一种基本数据类型,默认值是0;
  2. Integer变量必须实例化后才能使用,而int变量不需要 ;
  3. Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 ;

关于Integer的比较问题:

  1. Integer与new Integer()不相等,因为内存地址不一样,使用 == 比较都为false。
  2. 两个都是非new出来的Integer,使用 == 比较,如果数在-128到127之间,则是true,否则为false
  3. 两个都是new出来的,==比较都为false。若要比较值是否相等,需使用equals方法进行比较。
  4. int和Integer(无论是否new)比,都为true,因为会把Integer自动拆箱为int再去比。

13. 在⼀个静态⽅法内调⽤⼀个⾮静态成员为什么是⾮法的?

由于静态⽅法可以不通过对象进⾏调⽤,因此在静态⽅法⾥,不能调⽤其他⾮静态变量,也不可以访问⾮静态变量成员。

14. ⼀个类的构造⽅法的作⽤是什么? 若⼀个类没有声明构造⽅法,该程序能正确执⾏吗? 为什么?

⼀个类的构造⽅法的主要作⽤是完成对类对象的初始化⼯作。
若⼀个类没有声明构造⽅法,该程序也能执⾏。因为⼀个类即使没有声明构造⽅法也会有默认的无参构造⽅法。

15. 在 Java 中定义无参构造方法的作⽤

Java在调⽤⼦类构造⽅法之前,会先调⽤⽗类没有参数的构造⽅法,帮助⼦类做初始化⼯作。此时,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中的无参构造方法。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到无参构造⽅法可供执⾏。解决办法是在⽗类⾥显式加上⼀个无参构造⽅法。

在创建类时候,可以不用写构造方法,系统会默认提供一个无参构造方法,但是如果一旦提供了一个有参的构造方法,同时又没有显式的提供一个无参的构造方法,那么默认的无参的构造方法也没有了。

16. import java 和 javax 有什么区别?

刚开始的时候 Java API 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使⽤。然⽽随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java包确实太麻烦了,最终会破坏⼀堆现有的代码。因此,最终决定将 javax 也变为标准 API 的⼀部分。

17. 接⼝和抽象类的区别是什么?(考察1次)

抽象类: 在类中声明一个方法,这个方法没有实现体,是一个“空”方法,这样的方法就叫抽象方法,使用修饰符“abstract",当一个类有抽象方法的时候,该类必须被声明为抽象类。当然,一个类也可以在不提供抽象方法的前提下,声明为抽象类,一旦一个类被声明为抽象类,就不能够被直接实例化。

接口和抽象类相同点和区别:

相同点:

  1. 接口与抽象类都不能被实例化,需要被其他类进行实现或继承;
  2. 接口与抽象类里面都能包含抽象方法,实现接口或继承抽象类的子类都必须实现这些抽象方法;

不同点:

  1. 继承方面:
    抽象类只能单继承;接口可以多实现;
  2. 成员属性方面:
    抽象类中可以有普通属性,也可以有常量;
    接口中的成员变量全部默认是常量,使用public static final修饰,当然public static final也可以省略不写,是默认的;
  3. 代码块方面:
    抽象类可以含有初始化块;接口不能含初始化块
  4. 构造函数方面:
    接口不能有构造函数;
    抽象类可以有构造函数,但是这里的构造函数不是用来创建对象的,而且用来被实现类调用进行初始化操作的;
  5. 方法方面:
    接口里面不能定义静态方法;抽象类里面可以定义静态方法。
    接口里面只能是抽象方法;抽象类里面可以有抽象方法也可以有普通方法。

18. 成员变量与局部变量的区别?

  1. 在类中的位置
    成员变量:类中,方法外;
    局部变量:方法中或者方法声明上(形参);
  2. 作用范围
    成员变量:类中;
    局部变量:方法中;
  3. 初始化值
    成员变量:有默认值;
    局部变量:没有默认值。必须先定义,赋值,最后使用;
  4. 在内存中的位置不同
    成员变量:堆内存;
    局部变量:栈内存;
  5. 生命周期不同
    成员变量:随着对象的创建而存在,随着对象的消失而消失;
    局部变量:随着方法的调用而存在,随着方法的调用完毕而消失;

19. 对象实例与对象引⽤有何不同?

创建⼀个对象使用new 运算符,new 创建对象实例(对象实例在堆内存中),对象引⽤指向对象实例(对象引⽤存放在栈内存中)。⼀个对象引⽤可以指向 0 个或 1 个对象,⼀个对象可以有 n 个引⽤指向它。

20. 构造⽅法有哪些特性?

通过一个类创建一个对象,这个过程叫做实例化,实例化是通过调用构造方法实现的。

  1. 名字与类名相同。
  2. 没有返回值,但不能⽤ void 声明构造函数。
  3. ⽣成类的对象时⾃动执⾏,⽆需调⽤。

21. 静态⽅法和实例⽅法有何不同

  1. 在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。⽽实例⽅法只有后⾯这种⽅式。也就是说,调⽤静态⽅法可以⽆需创建对象。
  2. 静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不允许访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。

22. == 与 equals(考察2次)

== : 它的作⽤是判断两个对象的地址是不是相等。

equals() ⼀般有两种使⽤情况:

  1. 当前类没有重写Object类的equals() ⽅法。则通过 equals() ⽐较该类的两个对象时,等价于通过“==”⽐较这两个对象。
  2. 当前类覆盖了 equals() ⽅法,用来⽐较两个对象的内容是否相等;若它们的内容相等,则返回 true。

以String为例,String 中的 equals ⽅法是被重写过的,object 的 equals ⽅法是⽐较的对象的内存地址,⽽ String 的 equals ⽅法⽐较的是对象的值。

23. hashCode 与 equals

hashCode():
hashCode() 函数的作⽤是获取哈希码,也称为散列码;它实际上是返回⼀个 int 整数。这个哈希码的作⽤是确定该对象在哈希表中的索引位置。hashCode() 定义在Object.java 中,这就意味着 Java中的任何类都包含有 hashCode() 函数。

为什么要有 hashCode()?
hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址,hash类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,后判断equals(),即hashcode不等,equals一定不等,使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率。

以HashSet为例,HashSet不能添加重复的元素,当调用add方法时候,首先会调用hashCode方法判该元素的hashCode是否已经存在,如不存在则直接插入元素;如果存在,会接着调用equals方法来检查该对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。

为什么重写 equals 时必须重写 hashCode⽅法?
如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。

24. final 关键字

final 关键字主要⽤在三个地⽅:变量、⽅法、类。

  1. 对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
  2. 当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。
  3. final修饰方法,使其不能够被重写。

25. Java 中的异常处理

在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。Throwable有两个重要的⼦类:Error(错误)和Exception(异常)。

异常和错误的区别:异常能被程序本身处理,错误是⽆法处理。

Error(错误): 是程序⽆法处理的错误。⼤多数错误表示代码运⾏时 JVM出现的问题。这些错误是不可查的,因为它们在应⽤程序的控制和处理能⼒之 外,⽽且绝⼤多数是程序运⾏时不允许出现的状况。在这些异常发⽣时,JVM⼀般会选择线程终⽌。
例如,Java 虚拟机运⾏错误(Virtual Machine Error)、StackOverFlowError、OutOfMemoryError(JVM 不再有继续执⾏操作所需的内存资源)等。

**Exception(异常)😗*是程序本身可以处理的异常。Exception 类有⼀个重要的⼦类Runtime Exception。Runtime Exception 异常由 Java 虚拟机抛出。例如:Null Pointer Exception(要访问的变量没有引⽤任何对象时,抛出该异常)和 ArrayIndexOutOfBoundsException(下标越界异常)。

Throwable 类常⽤⽅法
public string getMessage():返回异常发⽣时的简要描述;
public string toString():返回异常发⽣时的详细信息;
public string getLocalizedMessage():返回异常对象的本地化信息。使⽤ Throwable 的⼦类覆盖这个⽅法,可以⽣成本地化信息。如果⼦类没有覆盖该⽅法,则该⽅法返回的信息与getMessage()返回的结果相同;
public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息;

异常处理总结

try 块: ⽤于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟⼀个finally 块。

catch 块: ⽤于处理 try 捕获到的异常。

finally 块: ⽆论是否捕获或处理异常,finally 块⾥的语句都会被执⾏。当在 try 块或catch 块中遇到 return 语句时,finally 语句块将在⽅法返回之前被执⾏。当 try 语句和 finally 语句中都有 return 语句时,在⽅法返回之前,finally 语句的内容将被执⾏,并且 finally 语句的返回值将会覆盖原始的返回值。

在以下 4 种特殊情况下,finally 块不会被执⾏:

  1. 在 finally 语句块第⼀⾏发⽣了异常。
  2. 在前⾯的代码中⽤了 System.exit(int)已退出程序。
  3. 程序所在的线程死亡。
  4. 关闭 CPU。

26. 序列化与反序列化

序列化与反序列化
序列化:将对象转化为二进制,用于保存,或者网络传输。
反序列化:与序列化相反,将二进制转化成对象。

序列化的作用:
① 想把内存中的对象保存到一个文件中或者数据库中时候;
② 想用套接字在网络上传送对象的时候;
③ 想通过RMI传输对象的时候
一些应用场景,涉及到将对象转化成二进制,序列化保证了能够成功读取到保存的对象。

序列化的实现:
要实现对象的序列化,最直接的操作就是实现Serializable接口。使用IO流中的对象流可以实现序列化操作,将对象保存到文件,再读取出来。

Java 序列化中如果有些字段不想进⾏序列化,怎么办?
对于不想进⾏序列化的变量,使⽤ transient 关键字修饰。
transient 关键字的作⽤是:阻⽌实例中那些⽤此关键字修饰的的变量的序列化操作;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和⽅法。

25 获取⽤键盘输⼊常⽤的两种⽅法

⽅法 1:通过 Scanner

Scanner input = new Scanner(System.in);
String s  = input.next Line();
input.close();

⽅法 2:通过 Buffered Reader

Buffered Reader input = new Buffered Reader(new 
Input Stream Reader(System.in));
String s = input.read Line();

26. Java 中 IO 流

按照流的流向分,可以分为输⼊流和输出流;
按照操作单元划分,可以划分为字节流和字符流;

Java IO流的 40 多个类都是从如下 4 个抽象类基类中派⽣出来的。
InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

既然有了字节流,为什么还要有字符流?
将字节流转换为字符流的过程⾮常耗时,并且,如果不知道编码类型就很容易出现乱码问题。所以, I/O 流就⼲脆提供了⼀个直接操作字符的接⼝,⽅便我们平时对字符进⾏流操作。如果⾳频⽂件、图⽚等媒体⽂件⽤字节流⽐较好,如果涉及到字符的话使⽤字符流⽐较好。

27. 深拷⻉和浅拷⻉的区别(考察3次)

  1. 浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏像引⽤传递一样的拷⻉。
  2. 深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容。

28. BIO NIO AIO的区别

首先来介绍一下同步和异步:
同步(阻塞): 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步(非阻塞): 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。

BIO: (Blocking I/O) - 阻塞IO,也叫传统IO。

  1. 先将文件内容从磁盘中拷贝到操作系统buffer;
  2. 再从操作系统buffer 拷贝到 程序应用(应用层)buffer;
  3. 从程序buffer拷贝到socket buffer;
  4. 从socket buffer拷贝到协议引擎;
    传统IO慢的原因是因为,数据捣腾过好几遍,才走到socket中。

NIO (New I/O) - 非阻塞IO
nio 是New IO 的简称,提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O。
NIO不经过应用层的缓存,数据直接放在操作系统的缓存,应用层只是直接发送指令,让操作系统Read buffer 直接写到Socket Buffer .少了内核到应用层之间过渡。

AIO (Asynchronous I/O) - 异步IO
AIO才叫做异步IO,NIO其实并不是真正意义上的异步IO,NIO只是非阻塞IO。
AIO连系统缓存buffer都省了,在外层建立NMAP的映射,数据地址用一个内存地址,地址不需要倒腾复制。直接读进来直接出去使用。

29. comparable 和 Comparator

comparable接⼝:
出⾃java.lang包 它有⼀个 compareTo(Object obj) ⽅法⽤来排序。
comparator接⼝:
出⾃ java.util 包它有⼀个 compare(Object obj1,Object obj2) ⽅法⽤来排序。
当我们需要进行⾃定义排序时,就要重写 compareTo() ⽅法或 compare() ⽅法。

30. 排序算法时间复杂度和空间复杂度

在这里插入图片描述

31. Java中创建对象的方式(考察1次)

在这里插入图片描述
1. 使用new关键字
这是创建一个对象最通用、常规的方法,同时也是最简单的方式。通过使用此方法,我们可以调用任何要调用的构造函数(无参的和带参的,默认使用无参构造函数)。

2. 使用Class类的newInstance方法
使用Class类的newInstance方法创建对象。这个newInstance方法调用无参的构造函数创建对象。

3.使用Constructor类的newInstance方法
和Class类的newInstance方法很像, java.lang.reflect.Constructor类里也有一个newInstance方法可以创建对象。我们可以通过这个newInstance方法调用有参数的和私有的构造函数。

上面两个 newInstance() 方法都被称为创建对象的反射方法。实际上, Class 类的 newInstance() 方法内部使用的就是 Constructor 类的 newInstance() 方法。这也是为什么后者更受欢迎,并且也被 Spring、Hibernate、Structs 等不同的框架所使用的原因。

4. 使用 Clone() 方法
每当我们对任何对象调用 clone() 方法时,jvm 都会为我们创建一个新对象,并将前一个对象的所有内容复制到其中。使用 clone 方法创建对象不会调用任何构造函数。要使用clone方法,我们需要先实现Cloneable接口并实现其定义的clone方法。

5. 使用反序列化(deserialization)
当我们序列化和反序列化对象时,JVM会为我们创建了一个独立的对象。在 反序列化时,JVM 不使用任何构造函数来创建对象。为了反序列化一个对象,需要让类实现Serializable接口。

两种newInstance方法的区别

  1. Class类位于java的lang包中,而构造器类是java反射机制的一部分。
  2. Class类的newInstance只能触发无参数的构造方法创建对象,而构造器类的newInstance能触发有参数或者任意参数的构造方法来创建对象。
  3. Class类的newInstance需要其构造方法是共有的或者对调用方法可见的,而构造器类的newInstance可以在特定环境下调用私有构造方法来创建对象。
  4. Class类的newInstance抛出类构造函数的异常,而构造器类的newInstance包装了一个InvocationTargetException异常。
  5. Class类本质上调用了反射包构造器类中无参数的newInstance方法,捕获了InvocationTargetException,将构造器本身的异常抛出。

32. 类中的变量、初始化块、构造器执行顺序(考察1次)

执行顺序:

  1. 一个类中的初始化顺序
    类内容(静态变量、静态初始化块) => 实例内容(变量、初始化块、构造器)

  2. 两个具有继承关系类的初始化顺序
    父类的(静态变量、静态初始化块)=> 子类的(静态变量、静态初始化块)=> 父类的(变量、初始化块、构造器)=> 子类的(变量、初始化块、构造器)

  3. 静态代码块和静态变量按声明先后次序加载。

33.反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性(包括私有的方法和属性);这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
它的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下的API以达到各种动态需求。

反射的优缺点:
优点: 使用反射,我们就可以在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

反射的用途:

  1. 反编译:.class–>.java
  2. 通过反射机制访问java对象的属性,方法(构造方法)等。
  3. 当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
  4. 反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
  5. 加载数据库驱动的,用到的也是反射。
    Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动
  6. 通过反射越过泛型检查:泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的。

反射机制常用的类:
在JDK中,由一些类来实现Java反射机制,这些类(除了Java.lang.Class外)都位于java.lang.reflect包中。

Java.lang.Class; 代表一个类,位于java.lang包下。
Java.lang.reflect.Constructor; 代表类的构造方法。
Java.lang.reflect.Field; 代表类的成员变量(成员变量也称为类的属性)。
Java.lang.reflect.Method; 代表类的方法。
Java.lang.reflect.Modifier; Modifier类提供一组静态方法用于解码类中的修饰符。
java.lang.reflect.Array;提供了动态创建数组,以及访问数组的元素的静态方法。

34. 获取Class对象的3种方式

要想使用反射,首先需要获得待操作的类所对应的Class对象。Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。

(1)Object类的getClass()方法,getClass()方法定义在Object类中,它不是静态方法,需要通过对象来调用,并且它声明为final,表明不能被子类所覆写。
(直接print所获得的Class对象classType会输出:class 完整类名,如果调用该Class对象的getName()方法,则输出完整类名,不加class。)

		Student stu1 = new Student();
		Class stuClass = stu1.getClass();
		System.out.println(stuClass.getName());

(2)使用类的.class语法,任何数据类型(包括基本的数据类型)都有一个class属性。

		Student stu1 = new Student();
		Class stuClass2 = Student.class;

(3)使用Class类的静态方法,forName(String className)(这也是最常用的)。

try {
	Class stuClass3 = Class.forName("fanshe.Student");
	//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}

注意,在运行期间,一个类,只有一个Class对象产生,即这三种方式获取的是同一个Class对象。

这三种方式中,常用第三种,对于第一种方式,对象都有了,就不需要反射了,第二种需要导入类包,依赖太强,不导包就抛编译错误。一般都使用第三种,字符串可以传入也可以写在配置文件中。

判断是否为某个类的实例:
一般的,我们使用instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例。

public native boolean isInstance(Object obj);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值