Java原理

1、面向对象与面向过程的区别:

  面向过程:以过程为中心,将问题分为多个步骤,按步骤依次完成。

  面向对象:将构成问题的事务分解成各个对象,建立对象也不是为了完成某个步骤,而是描述某个事务在解决问题步骤中的行为。

 

2、JVM:java字节码的虚拟机

  字节码:在一定程度上解决了传统翻译型语言的效率问题,同时也保留了解释型语言可移植的特点。

  .java (源代码)-JDK中javac编译-> .class  -JVM-> 机器码

    .class->机器码:类加载器首先加载字节码文件,然后翻译器逐行解释执行。 (对于经常用到的方法和代码块,使用JIT编译(即时编译器),属于运行时编译,编译一次之后字节码对应的机器码会被保存) ---java是编译与翻译共存的语言。

 

3、Java内存区域:(运行时数据区)

  java虚拟机在运行时会将其管理的内存分为多个区域:

    线程共享:堆、方法区 (方法区-元空间)

    线程私有:虚拟机栈、本地方法栈、程序计数器

      堆:存放对象实例和数组;-Xmx -Xms

      方法区:加载的类信息,常量,静态变量,即时编译器编译后的代码 (堆的逻辑部分)。(1.8 -> 元空间),元空间是在直接内存中。

      程序计数器:当前线程所执行的字节码的行号指示器,记录正在执行的虚拟机字节码地址。 (通过移动程序计数器来选取下一条字节码指令,在切换线程之后能恢复到正确的执行位置)

      虚拟机栈:java方法执行的内存模型,用于存储局部变量(基本数据类型和对象引用),操作数栈,动态链接,方法出口等信息。随着线程创建而创建,随着线程死亡而死亡。每一次方法的调用都会压入一个栈帧。

      本地方法栈:虚拟机用到的本地方法服务。

      

      运行时常量池:字面量(文本字符串、声明为final的常量等),符号引用(类和接口的限定名,字段名称和描述符,方法名称和描述符)(1.7在方法区,1.8在堆)

 

    常量池:

      静态常量池:.class文件中常量池,字面量和符号引用量。

      运行时常量池:jvm完成类加载后,常量池被保存在了方法区。

      

 

4、Java对象创建:

  类加载检查 -- 分配内存 -- 初始化零值 -- 设置对象头 -- 执行init方法

    类加载检查:检查这个指令的参数是否能在常量池里定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。

    分配内存:指针碰撞(用过的内存放到一变)、空闲列表

    初始化零值:将分配到的内存空间都初始化为零(除了对象头)

    设置对象头:哪个类的实例,GC分代年龄,对象哈希码。(mark word,类型指针)

    init方法:按照程序员的意愿进行初始化。

  对象的内存布局:对象头(哈希码、G分代年龄、锁状态标识、线程持有的锁。。。)+ 实例数据 + 补齐填充

  对象访问定位:

    句柄访问:对象引用存放的是句柄的地址,堆中开了内存来存放句柄所对应的对象地址

    直接指针访问:对象引用存放的是对象的地址

 

5、Java垃圾回收:

  垃圾回收是回收死亡的对象,管理的是堆区域,垃圾回收一般使用的是分代回收算法。

  GC堆:堆是垃圾收集器管理的区域,所以堆是GC堆。

  垃圾收集器:大部分使用分代回收算法 (新生代,老年代)

    标记清除算法:先标记,然后收回被标记对象的内存空间

    复制:讲内存分为两块,垃圾回收是将活的对象复制到另一块内存中。

    标记整理算法:完成标记清除之后,需要将活的对象移到一端。

    分代回收:根据新生代和老年代不同的特征来选择不同的垃圾回收机制。一般来说,新生代选择复制机制,老年代选择标记清除或者标记整理机制。

    eden-from-to-tentired:对象首先分配到eden区域,第一次回收后,活的对象 年龄加一 并放到 from to区域,查过一定岁数会放到老年区 (大对象一般也在老年区)

  

  判断对象死亡:

    (1)引用计数器(难以解决循环引用问题)

    (2)可达性分析算法:将GC Roots 对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。如果一个对象到GC Roots没有引用链指向,则对象不可用。

       GC Roots对象:虚拟机栈中引用对象,方法区(堆)中静态属性引用的对象,方法区(堆)中常量引用的对象,本地方法栈中引用对象。

  强引用、软引用、弱引用、虚引用:

    强引用:大部分引用都是强引用

    软引用:当内存不足时,回收对象内存

    弱引用:垃圾回收时,回收对象内存

    虚引用:用来跟踪对象的垃圾回收的活动,在这个对象被垃圾收集器回收时收到一个系统通知。

  不可达需要两次标记。

  1)没有引用链并且finalize()被覆盖,放入队列中

  2)在finalize() 方法中,如果队列中的对象被赋给某个类变量或者对象的成员变量,则移出”即将收回“集合

  

  垃圾收集器:serial、cms

    serial:单线程,暂停其他线程 (复制,标记整理)

    Parnew: serial 的多线程版本

    CMS:(标记-清除)初始标记  - 并发标记 - 重新标记 - 并发清除  关注停顿时间( 适用于与交互多的场景)  

    parallel scavenge:关注吞吐量(适用于后台运算多的场景)复制 (默认)

    parallel old:标记整理

  内存泄漏:程序不会再使用对象,但是依然还有引用链指向对象。

      对象依然指向数据成员,集合中依然存着对象:对象数据成员设为null或者从集合中移除该对象。

  Minor GC 和Full GC:

    Minor GC:新生代垃圾回收

    Full GC:所有堆空间垃圾回收

      引发Full GC:(1)System.gc(2)CMS (浮动垃圾)Concurrent Mode Failure-> serial old 所以不能等内存几乎被填满了才进行垃圾回收 (3) 无法找到足够大的连续空间来分配当前对象。(4)老年代剩余连续空间是否大于历代新生代转为老年代的平均大小

  频繁 Full GC: 大多数对象不符合“朝生夕灭”的原则,即大多数对象生存时间长。(主要的生存对象应该是请求级或者页面级,会话级和全局级的长生命对象应该相对很少

 

  虚拟机性能监控与故障处理:

    jps:查看虚拟机线程

    jstat:监视各种运行状态(gc等)

    info:java 配置信息

    jmap:生成堆转储快照

    jstack:java堆栈跟踪工具

 

6、类加载过程:

  加载-连接-初始化(执行构造器):

  加载:(1)通过类的全限定名来获取定义此类的二进制字节流(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(3)在内存中生成一个代表这个类的Java.lang.Class对象,作为方法去这个类的各种数据的访问入口

  验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求。(文件格式,元数据,字节码,符号引用)

  准备:正式为类变量分配内存并设置初始值(数据类型的零值)

  解析:将常量池中符号引用替换为直接引用的过程,

  初始化:类变量的赋值和静态代码块

 

  连接:验证 - 准备(分配内存并设置类变量初始值)- 解析(将符号引用替换为直接引用)

 

7、封装、继承、多态:

  封装:将一个类的属性私有化,并提供可以被外界访问属性的方法

  继承:以已存在的类的定义作为基础建立新类的技术,新的类的定义可以增添新的数据和功能。拥有类的所有数据和方法。

  多态:定义的引用变量所指对象的具体类型和通过该变量调用的方法编程时是不知道的,只有在程序运行时期才知道。

 

8、接口、抽象类

  接口:对行为的抽象  public 只能有常量(static、final)和抽象方法(public、default、protected)   在1.8之后能定义默认方法和静态方法  在9之后能定义私有的方法和私有静态方法

  抽象类:对类的抽象

抽象类

在Java中,被关键字abstract修饰的类称为抽象类;被abstract修饰的方法称为抽象方法,抽象方法只有方法声明没有方法体。
抽象类有以下几个特点:
1、抽象类不能被实例化,只能被继承。
2、包含抽象方法的类一定是抽象类,但抽象类不一定包含抽象方法(抽象类可以包含普通方法)。
3、抽象方法的权限修饰符只能为public或者protected,默认情况下为public。
4、一个类继承于一个抽象类,则子类必须实现抽象类的抽象方法,如果子类没有实现父类的抽象方法,那子类必须定义为抽象类。
5、抽象类可以包含属性、方法、构造方法,但构造方法不能用来实例化对象,只能被子类调用。

接口

接口可以看成是一种特殊的类,只能用interface关键字修饰。接口是提供给别人调用的。Java中的接口具有以下几个特点:
1、接口中可以包含变量和方法,变量被隐式指定为public static final,方法被隐式指定为public abstract(JDK 1.8之前);
2、接口支持多继承,即一个接口可以继承(extends)多个接口,间接解决了Java中类不能多继承的问题;
3、一个类可以同时实现多个接口,一个类实现某个接口则必须实现该接口中的抽象方法,否则该类必须被定义为抽象类;
4、JDK 1.8中对接口新增了两个特性:
(1)默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法。
(2)静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)。
5、在JDK 1.9中,再次对接口进行了增强,可以实现private method和private static method。因为JDK 1.8中可以对接口中的方法进行实现,那么当我们不希望实现的方法暴露给外部时,则可以将方法定义为private。

接口与抽象类的区别

1、抽象类中可以包含普通方法,但接口中只能包含public abstract方法(JDK 1.8之前);
2、抽象类中的成员变量没有访问权限的限制,但接口中的变量只能被public static final修饰;
3、一个接口可以继承多个接口,但一个类只能有一个父类,类可以实现多个接口;
4、抽象类是对一类事物的抽象,接口则是对行为的抽象。一个类继承一个抽象类代表“是不是”的关系,而一个类实现一个接口则表示“有没有”的关系。

 

9、public、default(包)、protected(子类)、private

 

10、hashCode():获取哈希码(散列码)。int类型。该哈希码确定了该对象在hash表的索引位置。

 

11、线程

  状态:初始状态(线程被构建),运行状态(就绪+运行),等待状态(需要其它的线程做出特定的指令:中断或通知),阻塞状态(线程阻塞于锁),等待超时状态(在指定 的时间自行返回)和终止状态(当前线程执行完毕)

   方法:

    start():开始执行

    notify():

    wait():线程进入等待状态,需要其它线程的通知才能返回到运行状态

    sleep(long millis),wait(long milis):time_waiting

 

12、序列化:

   transient:反序列化,修饰变量

 

13、用键盘输入:

  Scanner in = new Scanner(System.in);

  String s = in.nextLine();

 

  BufferedReader input = new BufferedReader(InputSreamReader(System.in));  

  String s = input.readLine();

 

14、BIO、NIO、AIO

  BIO:同步阻塞IO,数据的读取和写入必须阻塞在一个线程里等待其完成。

  NIO:同步非阻塞,面向缓存区 Cannal,Selector Buffer等

  AIO:异步

 

15、反射机制:

  在程序运行时,对于任意一个类,能知道任何类的方法和属性,能调用其方法和属性。动态地获取信息或动态调用方法:反射机制。

  Class对象:将一个类的方法、变量告诉运行程序。

    获取class对象:

        Class TargetClass = TargetObject.class

        Class TargetClass = Class.forName("java.util.ArrayList")

    

        TargetClass.newInstance();  //用class对象创建实例:

        getDeclaredMethods(); //获得方法

 

  Class.forName 加载的类已经初始化了。

  ClassLoader.loadeClass 加载的类只是加载了,还没有进行链接。

 

  优点:动态加载类,代码灵活度高

  缺点:1、性能:比直接用java代码获取类慢很多 2、安全:改变属性,增加了类的安全隐患

  应用场景:

    JDBC数据库连接:

    Spring框架IOC创建Bean和AOP(动态代理)功能

 

16、不使用new关键字获得对象

  反射:forName()  -> calss.newInstance()(Class对象) 或者 calss.getConstructor.newInstance()(构造函数对象)

  clone():object.clone()

  反序列化:objectInputSream.readObject()

          

17、Java新特性

  (1)接口:增加了默认方法

  (2)lambda表达式

  (3)Streams流:stringList.stream().filter((s-> s.startsWith("a")).forEach(System.out::println);

  (4)多重注解

 

18、关键字

  final:变量,类,方法

    (1)变量:必须显示地赋值,并且初始化之后不能再更改。基本类型:不能再修改值;引用类型:不能再指向其他对象。成员变量:在声明或构造函数中初始化,局部变量:在使用之前初始化。

    (2)类:不能被继承,且类中方法默认为final

    (3)方法:表示此方法是最终的,即方法不可以被重写。如果父类的final方法为private,子类使用相同方法名则是重新定义方法。

 

  static:变量,方法,代码块,内部类

    (1)变量:静态变量。属于类,通过类名调用

    (2)方法:属于类,通过类名调用

    (3)静态代码块静态代码块-非静态代码块-构造函数),不管创建多少对象,静态代码块只执行一次。在JVM加载类时,会执行这些代码块。

    (4)静态内部类。静态内部类和非静态内部类区别在于,非静态类在编译完成后,会有一个指向创建它的外围类。即它的类不需要外围类的创建,也不能使外围类的非静态成员或方法。

    (5)静态包。用来指定包中的某个静态资源。

 

  this:引用当前类

 

  super:访问父类的方法和属性。 super和this在构造其中需要放在首行。

 

 

19、浅拷贝 vs深拷贝

  浅拷贝:对于基本类型使用值传递,对于引用类型进行引用传递,该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。System.arraycopy 浅拷贝;

  深拷贝:对于基本类型,会创建一个新的对象,并复制给新的对象。每一层的每个对象都进行浅拷贝=深拷贝

 

20、装箱与拆箱

  自动装箱:Integer.valueOf(1) 自动将基本来行转换为包装类型

  自动拆箱:Integer.intValue() 自动将包装类型转换为基本类型     == 算数运算

 

  对于Integer:基本类型自动装箱为Integer,如果数值在【-128,127】之间那么就返回常量池里的对象,否则,每次装箱都创建一个新的对象 (Integer、Short、Byte、Character、Long类似)

  对于Boolean:每次装箱都为同一个对象

 

  

 

Integer i = new Integer(xxx); //不会触发自动装箱,一定会返回一个全新的对象
Integer j = xxx;//会自动装箱,在【-128, 127】之间,会返回cache中的对象。

 

 

21、equals() 和 ==

  equals():(1)没有覆盖:等价于== (2)覆盖了,比较值  

  ==:基本类型比较值,对象比较地址

 

22、hashCode() 和 equals()

  hashCode():得到对象的hash码,确定该对象在hash表上的位置

  equals() 和hashCode() 只有在散列表中才有关系:在散列表中,有相同hash值的equals()不一定相等;equals()相同的,hash值一定相等

 

  hashMap中的put:(1)hash值对应的位置是否有元素,如果没有直接put (2)如果对应的位置有元素,且key值相同,修改value(3)如果对应的位置有元素,且key值不相同,如果元素个数 < 64,则扩容,否则将对象放到链表中

  

23、重写和重载

  重写:子类对父类的方法进行重新编写。

  重载:同一个类中,多个同名方法根据不同的传参来执行不同的逻辑处理

    

24、成员变量与局部变量

  语法:

  存储方式:成员变量(堆) 局部变量(虚拟机栈)

  生存时间:成员变量与类的生存时间一样,局部变量和调用的方法生存时间一样

  赋初值:成员变量有默认初始值,局部变量没有

posted @ 2020-04-04 10:44  lucy_cui  阅读(163)  评论(0)    收藏  举报