基本数据类型
面向对象和面向过程的区别:
- 面向对象就是把生活中存在的事物以类的形式在java语言中描述出来,把事物的外在特征以属性的形式来表示,把事物的行为功能以方法的形式表示出来,这样就把一个实实在在的物体在java程序中虚构出来了!
-
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
- 面向过程优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
- 缺点:没有面向对象易维护、易复用、易扩展
- 面向对象优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
- 缺点:性能比面向过程低
java和C++区别
- Java是解释型语言,所谓的解释型语言,就是源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码。对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了。
- C++是编译型语言,所谓编译型语言,就是源码一次编译,直接在编译的过程中链接了,形成了机器码。
- C++比Java执行速度快,但是Java可以利用JVM跨平台(java一次编写,到处运行)。
- Java是纯面向对象的语言,所有代码(包括函数、变量)都必须在类中定义。而C++中还有面向过程的东西,比如是全局变量和全局函数。
- C++中有指针,Java中没有,但是有引用。
- C++支持多继承,Java中类都是单继承的。但是继承都有传递性,同时Java中的接口是多继承,类对接口的实现也是多实现。
- C++中,开发需要自己去管理内存,但是Java中JVM有自己的GC机制,虽然有自己的GC机制,但是也会出现OOM和内存泄漏的问题。C++中有析构函数,Java中Object的finalize方法。
java序列化
我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术。
Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制数据保存到磁盘或者传输网络,磁盘或者网络接收者可以在对象的属类的模板上来反序列化类的对象,达到对象持久化的目的。
什么是 Java 的序列化,如何实现 Java 的序列化?
- 对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序。从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。序列化是为了解决在对象流进行读写操作时所引发的问题。
- 序列化的实现:将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,只是用于标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream 对象,接着使用 ObjectOutputStream 对象的 writeObject(Object obj) 方法可以将参数为 obj 的对象写出,要恢复的话则使用输入流。
什么情况下需要序列化?
- 当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
- 当你想用套接字在网络上传送对象的时候;
- 当你想通过 RMI 传输对象的时候。
序列化注意事项
- 序列化对象必须实现序列化接口。
- 序列化对象里面的属性是对象的话也要实现序列化接口。
- 类的对象序列化后,类的序列化ID不能轻易修改,不然反序列化会失败。
- 类的对象序列化后,类的属性有增加或者删除不会影响序列化,只是值会丢失。
- 如果父类序列化了,子类会继承父类的序列化,子类无需添加序列化接口。
- 如果父类没有序列化,子类序列化了,子类中的属性能正常序列化,但父类的属性会丢失,不能序列化。
- 用Java序列化的二进制字节数据只能由Java反序列化,不能被其他语言反序列化。如果要进行前后端或者不同语言之间的交互一般需要将对象转变成Json/Xml通用格式的数据,再恢复原来的对象。
- 如果某个字段不想序列化,在该字段前加上transient关键字即可。
类的加载顺序
- 父类静态优先(父类静态的东西按顺序执行)
- 子类静态(子类静态的东西按顺序执行)
- 父类属性-父类代码块-父类构造器
- 子类属性-子类代码块-子类构造器
类什么时候被加载?
- new一个对象的时候
- 调用静态属性
- 调用静态方法
- 加载驱动 Class.forName();
Final修饰词
-
修饰数据:Final修饰的变量沦为常量,初始化之后不能再被修改,被final修饰的变量(属性)必须被初始化,初始化有3个地方定义时,块中, 构造器。
对于基本类型: final 使数值不变;
对于引用类型: final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 - 修饰方法:Final修饰的方法不能被重写
- 修饰类:Final修饰的类不能被继承
final、finally、finalize 的区别?
- final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。
- finally:异常处理语句的一部分,表示总是执行。
- finallize:Object类的一个方法,在垃圾回收时会调用被回收对象的finallize方法。
30、static 关键字的作用?
- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份;
- 静态方法:静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字;
- 静态语句块:静态语句块在类初始化时运行一次;
- 静态内部类:非静态内部类依赖于外部类的实例,而静态内部类不需要。静态内部类不能访问外部类的非静态的变量和方法;
- 初始化顺序:静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
多态:
多态性是面向对象编程的一种特性,和方法无关。简单说,就是同样的一个方法能够根据输入数据的不同,做出不同的处理,即方法的重载——有不同的参数列表(静态多态性)
而当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法,即在子类中重写该方法——相同参数,不同实现(动态多态性)
向上转型要注意几点
- 当发生向上转型时,子类独有的方法用不成
- 当发生向上转型后,调用的方法只和new的对象有关(动态绑定)
- 当发生向上转型后,调用的属性只和类型有关(静态绑定)
- staitc修饰的方法只和类型有关,staic修饰的东西属于静态的,属于类成员,还一个叫实例成员
- 向下转型:把父类强制转换成父类,向下转型的前提是先向上转型
接口和抽象类的区别:
- 接口中只能有抽象方法,抽象类中可以有抽象方法,也可以有普通方法
- 接口中不能有构造器,抽象类中可以有构造器
- 接口和抽象类都不能实例化
- 抽象类之间是单继承,接口之间是多继承
- 抽象类不能被final修饰,抽象类中的方法不能被static,final修饰,接口中的属性默认被public static final修饰,方法默认被public abstract修饰
- 非抽象子类中都必须全部实现接口和抽象类中的所有抽象方法
- 抽象类中可以有代码块,接口中没有
String,StringBuffer,StringBuilder
- StringBuffer多线程安全的,StringBuilder多线程不安全.
- StringBuffer从JDK1.0就有了,StringBuilder是JDK5.0才出现。
StringBuffer多线程安全,但是加了synchronized,其效率低。故适用于多线程下,并发量不是很高的场景
StringBuilder没有加任何锁,其效率高,适用单线程场景,但同时也适用于高并发场景中,提高高并发场景下程序的响应性能,至于线程安全问题可以通过其它手段解决,如ThreadLocal,CAS操作等。 - StringBuffer比StringBuilder多了一个toStringCache字段,用来在toString方法中进行缓存,每次append操作之前都先把toStringCache设置为null,若多次连续调用toString方法,可避免每次Arrays.copyOfRange(value, 0, count)操作,节省性能。
- 由于StringBuilder没有考虑同步,在单线程情况下,StringBuilder的性能要优于StringBuffer.
Java 的泛型是如何工作的 ? 什么是类型擦除 ?
- 泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如:List 在运行时仅用一个 List 来表示。这样做的目的,是确保能和 Java 5 之前的版本开发二进制类库进行兼容。
- 类型擦除:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 则会被转译成普通的 Object 类型,如果指定了上限如 则类型参数就被替换成类型上限。
如何实现对象的克隆?
- 实现 Cloneable 接口并重写 Object 类中的 clone() 方法;
- 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
深克隆和浅克隆的区别?
- 浅克隆:拷贝对象和原始对象的引用类型引用同一个对象。浅克隆只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅克隆。
- 深克隆:拷贝对象和原始对象的引用类型引用不同对象。深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse() 和 JSON.stringify(),但是此方法无法复制函数类型)。
Java 中创建对象的几种方式?
- 使用 new 关键字;
- 使用 Class 类的 newInstance 方法,该方法调用无参的构造器创建对象(反射):Class.forName.newInstance();
- 使用 clone() 方法;
- 反序列化,比如调用 ObjectInputStream 类的 readObject() 方法。
Java 中的反射是什么意思?有哪些应用场景?
- 每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 Class.forName(“com.mysql.jdbc.Driver”) 这种方式来控制类的加载,该方法会返回一个 Class 对象。
- 反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
(1)Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
(2)Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
(3)Constructor :可以用 Constructor 创建新的对象。 - 应用举例:工厂模式,使用反射机制,根据全限定类名获得某个类的 Class 实例。
反射的优缺点?
- 优点:运行期类型的判断,class.forName() 动态加载类,提高代码的灵活度;
-
缺点:尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。
(1)性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
(2)安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
(3)内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如:访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
集合相关
数组和集合的区别
- 数组里面只能存放相同类型的数据,集合里面可以存放任意类型的数据
- 数组长度固定,集合长度无限制
- 数组定义什么类型就放什么类型,集合只能放类类型
Collection(接口)
-
List: 特点有顺序(加入顺序和输出顺序一致),能重复,只有list分支可以通过下标去访问
- ArrayList:特点查询速度快,增删效率慢, 底层采用数组实现,数组使用的是队列形式.(默认长度是10,放满时每次容量增加为原来的一半)
- LinkedList:特点查询速度慢,增删效率快, 底层采用链表实现。
- Vector向量:有大小,有方向的一个箭头。是我们集合中最早的一个集合类,正因为它出来的很早,所以现在过时了,被其它集合类替代了。
-
ArrayList和Vector的区别
- ArrayList线程不安全,Vector线程安全
- ArrayList当集合放满的时候每次容量增加为原来的一半,Vector每次增加为原来的一倍
-
Set:无顺序(加入顺序和输出顺序不一致),不能重复。
- HashSet:特点查询和增删效率都比较高,缺点是占用内存空间 Hashset底层使用hash码实现的。
-
Map:以键值对的方式存储数据。无顺序,键不能重复,值可以重复.
-
HashMap:键不允许重复,根据hashCode来判断的,再说就回到HashSet那去了
hashSet和hashMap底层都是使用hashCode来判断是否元素重复
hashSet的底层其实就是一个hashMap,这个hashMap静态代理了hashSet -
TreeMap
- TreeMap:可以对map集合的键进行排序
- HashTable:过时了,已经被hashMap替代
-
HashMap:键不允许重复,根据hashCode来判断的,再说就回到HashSet那去了
-
Collections:专门用来操作集合
- Sort()升序排序
- Max() min()
- Shuffle(),随机打乱集合中的元素
- Swap()交换集合中两个元素的位置
- frequency(list, 3):返回指定元素在集合中出现的个数
IO流
字节流和字符流有什么区别?
- 字节流按 8 位传输,以字节为单位输入输出数据,字符流按 16 位传输,以字符为单位输入输出数据。但是不管文件读写还是网络发送接收,信息的最小存储单元都是字节。
BIO、NIO、AIO 有什么区别?
- BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
- NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。NIO 是一种同步非阻塞的 I/O 模型,在 Java1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。NIO 提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。1.java NIONIO是一种非阻塞同步的通信模式,以块的方式处理数据服务器实现模式为一个请求一个线程