面向过程: 面向过程性能比面向对象高 。因为对象调用需要实例化,开销比较大,较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等,一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
面向对象: 面向对象易维护、易复用、易扩展 。因为面向对象有封装、继承、多态性的特性,所以可设计出低耦合的系统,使得系统更加灵活、更加易于维护。
面向过程也需要分配内存,计算内存偏移量,Java 性能差的主要原因并不是因为它是面向对象语言,而是因为 Java 是半编译语言,最终的执行代码并不是可以直接被 CPU 执行的二进制机器码。
而面向过程语言大多都是直接编译成机器码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比 Java 好。
当 .class 字节码文件通过 JVM 转为机器可以执行的二进制机器码时,JVM 类加载器首先加载字节码文件,然后通过解释器逐行进行解释执行,这种方式的执行速度相对比较慢。而且有些方法和代码块是反复被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成一次编译后,会将字节码对应的机器码保存下来,下次可以直接调用。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。
HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。
Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。
Java 的跨平台指的是 java 源文件经过 javac 编译器编译成的二进制.class 字节码的跨平台性。各个平台装有不同的 jvm,而 jvm 能将相同的字节码翻译成平台相关的机器码,进而执行。
Java 运行时环境(JRE)。它包括 Java 虚拟机、Java 核心类库和支持文件。它不包含开发工具(JDK)--编译器、调试器和其他工具。 Java 开发工具包(JDK)是完整的 Java 软件开发包,包含了 JRE,编译器和其他的工具(比如:JavaDoc,Java 调试器),可以让开发者开发、编译、执行 Java 应用程序。
private:类成员只能被当前类本身访问,如果类的构造方法声明为 private,则其他类不能生成该类的实例。
default:类成员可以被这个类和同一个包中的类所访问。
protected:类成员被同一个类,同一个包中的其他类,子类(同包或不同包)访问。
public:被所有类访问。
8、”static”关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?
static 表示静态的意义,可以修饰成员变量、成员方法、内部类和代码块,被 static 修饰的变量叫做静态变量,随类加载一次,可以被多个对象共享。被其修饰的方法成为静态方法(类方法),其随着类的加载而被加载,可以通过类直接访问,不需要实例化对象。此外静态方法只能访问静态成员,不可以直接访问非静态成员。静态内部类可以直接调用静态构造器(不用对象)。静态代码块只需加载一次,可以有多个代码块。
重写是子类中的方法和子类继承的父类中的方法一样(函数名,参数,参数类型,返回值类型),但是子类中的访问权限要不低于父类中的访问权限。重写的前提是必须要继承,private 修饰不支持继承,因此被私有的方法不可以被重写。静态方法形式上可以被重写,但是会被隐藏。原因在于方法重写基于运行时动态绑定,而 static 方法是编译时静态绑定的。
因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以直接访问非静态的成员。
最常见的 static 方法就是 main,作为程序的入口,所有对象都是在该方法里面实例化。
自动装箱就是 Java 编译器在基本数据类型和对应的对象包装类型间的转化,即 int 转化为 Integer,自动拆箱是 Integer 调用其方法将其转化为 int 的过程
Java 中的构造函数是为了初始化对象的,构造函数的函数名和类名一致,默认的构造函数没有参数,没有返回值,构造函数的函数体内,没有内容。 构造函数的重载是函数名与类名相同,参数列表不同。同样也是为了初始化对象的。
关于复制构造函数:C++ 中的复制构造函数通常有三种作用
C++ 语法允许用户定义自己的复制构造函数以实现自定义的复制,比如说进行 深复制 。Java 并不支持这样的复制构造函数。但是这并不代表 Java 中没有这种机制,在 Java 中 Object 类的 clone()方法就是这种机制的体现。而且通过以上三种方式对 Java 对象进行的操作都是对引用的操作,不像 C++ 里面是对原对象的操作,因此 Java 中也不需要考虑需要使用复制构造函数这种问题。
Java 中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是 Java 中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。
Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
当基本型变量作为参数传递到方法中时,参数的值是该变量的副本,改变副本不影响原变量。
StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb 没有被改变,还是 "iphone"。 复制代码
推荐阅读: Java中只有按值传递,没有按引用传递!
基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 它们之间的比较,应用双等号(==),比较的是它们的值。
复合数据类型(类)。当它们用双等号进行比较的时候,比较的是它们在内存中的存放地址,所以,除非是同一个 new 出来的对象,它们的比较后的结果为 true,否则比较后结果为 false。 Java 当中所有的类都是继承于 Object 这个基类的,在 Object 中的基类中定义了一个 equals 的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如 String,Integer,Date 在这些类当中 equals 有其自身的实现(在重写 equals 方法的时候,有必要重写对象的 hashCode 方法,从而保证程序完整性),而不再是比较类在堆内存中的存放地址了。
hashCode()与equals()的相关规定:
为什么要有 hashCode?
我们先以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode: 当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相同的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
通过我们可以看出:hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
继续上面的话题,为什么必须要重写 hashcode 方法,其实简单的说就是为了保证同一个对象,保证在 equals 相同的情况下 hashcode 值必定相同,如果重写了 equals 而未重写 hashcode 方法,可能就会出现两个没有关系的对象 equals 相同的(因为 equals 都是根据对象的特征进行重写的),但 hashcode 确实不相同的。
操作字符串的类有: String
、 StringBuffer
、 StringBuilder
。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
indexOf():返回指定字符的索引。 charAt():返回指定索引处的字符。 replace():字符串替换。 trim():去除字符串两端空白。 split():分割字符串,返回一个分割后的字符串数组。 getBytes():返回字符串的 byte 类型数组。 length():返回字符串长度。 toLowerCase():将字符串转成小写字母。 toUpperCase():将字符串转成大写字符。 substring():截取字符串。 equals():字符串比较
public class Test03 { public static void main(String[] args) { Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2); System.out.println(f3 == f4); } //结果 true false 复制代码
Java 在编译 Integer i = 100 ;时,会翻译成为 Integer i = Integer.valueOf(100)。根据 Java API 中对 Integer 类型的 valueOf 的定义可知,当数值在[-128,127]范围内时,Integer 对象会存储在缓存中,f1 和 f2 都指向的是缓存中同一个对象,而不会 new 个新对象。反之如果不在该范围内,f3 和 f4 指向两个不同的对象。
Static Nested Class 是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
class Outer { class Inner {} public static void foo() { new Inner(); //编译报错 } public void bar() { new Inner(); } public static void main(String[] args) { new Inner();//编译报错 } } 复制代码
Java 中非静态内部类对象的创建要依赖其外部类对象,上述代码中 foo 和 main 方法都是静态方法,静态方法中没有 this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:new Outer().new Inner();
接口和抽象类的区别
相同点:
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被 synchronized 修饰?
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。
&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是 true 整个表达式的值才是 true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
节点流:可以从或向一个特定的地方(节点)读写数据。如 FileReader。处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
推荐阅读: JAVA的节点流和处理流
Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
按操作方式分类结构图:
按操作对象分类结构图:
BIO: Block IO 同步阻塞式 IO ,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO: New IO 同步非阻塞 IO ,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,在 Java 7 中引入了 NIO 的改进版 NIO 2,它是 异步非阻塞的IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。