JavaSE(扩展学习,面试可能问到)

Java跨平台原理

java提供了对应不同操作系统,不同位数的java虚拟机(JVM),屏蔽java程序在各种不同的硬件和操作系统中对内存的访问的差异,而保证对外统一的接口(java Api)。这样对于开发人员来说,只要按照JavaApi开发即可。如果需要部署到不同环境,只需要在该环境上安装对应版本的虚拟机即可。

Java内存模型

  1. PC寄存器/程序计数器:
    每个线程私有,用于记录当前线程执行到哪一步,意外中断后用于恢复。
  2. Java栈 Java Stack:
    Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧被创建,这个新创建的栈帧被放到Java栈的栈顶,变为当前的活动栈。同样现在只有这个栈的本地变量才能被使用,当这个栈帧中所有指令都完成时,这个栈帧被移除Java栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数。由于Java栈是与线程对应起来的,Java栈数据不是线程共有的,所以不需要关心其数据一致性,也不会存在同步锁的问题。在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。在Hot Spot虚拟机中,可以使用-Xss参数来设置栈的大小。栈的大小直接决定了函数调用的可达深度。java栈模型
  3. 堆 Heap:
    堆是JVM所管理的内存中最大的一块,是被所有Java线程锁共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域。
  4. 方法区Method Area:
    方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当在程序中通过Class对象的getName.isInterface等方法来获取信息时,这些数据都来源于方法区。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收,它存储的信息相对比较稳定,在一定条件下会被GC,当方法区要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。方法区也是堆中的一部分,就是我们通常所说的Java堆中的永久区 Permanet Generation,大小可以通过参数来设置,可以通过-XX:PermSize指定初始值,-XX:MaxPermSize指定最大值。
  5. 常量池Constant Pool:
    常量池本身是方法区中的一个数据结构。常量池中存储了如字符串、final变量值、类名和方法名常量。常量池在编译期间就被确定,并保存在已编译的.class文件中。一般分为两类:字面量和应用量。字面量就是字符串、final变量等。类名和方法名属于引用量。引用量最常见的是在调用方法的时候,根据方法名找到方法的引用,并以此定为到函数体进行函数代码的执行。引用量包含:类和接口的权限定名、字段的名称和描述符,方法的名称和描述符。
  6. 本地方法栈Native Method Stack:
    本地方法栈和Java栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

Java的三(四)大特性

  • 抽象:把一类事物的特性(属性)和行为(方法)进行归纳,抽取出来。
  • 封装:抽象后把特征和行为写到一个类中,这就是封装。封装是为了保证安全性(private),外部程序要调用封装好的对象属性,就需要通过指定的公开方法来获取。
  • 继承:优点是减少冗余的代码,提高代码复用性。缺点就是提高了代码的耦合度
    • 子类拥有父类非private的属性,方法。
    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
    • 子类可以用自己的方式实现父类的方法(重写方法)。
    • 只能单继承,但是可以多重继承。
  • 多态:满足这三个条件 1.有继承 2. 有重写 3. 要有父类引用指向子类对象(向上转型)。同一个行为具有多个不同表现形式或形态的能力
    1. 父类引用可以指向子类对象,子类引用不能指向父类对象。
    2. 把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。如Father father = new Son();
    3. 把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father;其中father前面的(Son)必须添加,进行强制转换。
    4. 向上转型会丢失子类特有的方法,但是对于子类重写的方法,会取子类重写后的。
    5. 向上转型的作用,减少重复代码,父类为参数,调用时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
    6. 变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。
    • 方法的重写:发生在子类继承父类的关系中,父类中的方法被子类继承,方法名,返回值类型,参数完全一样,但是方法体不一样那么说明父类中的该方法被子类重写了。重写的方法,访问修饰符的范围不能比原方法小。
    • 方法的重载:参数列表的不同(参数个数,类型,顺序的不同),可以构成方法重载。

Java的数据类型(参数传递方式)

基本数据类型:值传递
引用数据类型:地址传递
java数据类型
下图单位是bit,非字节 1B=8bit
基本数据类型
基本数据类型的默认值
String和基本数据类型的包装类型(比如Integer)直接赋值和new对象的区别:

  • 直接赋值:会先去常量池取,若常量池有,则直接引用,假如两次值是一样的,则==和equals都为true,因为直接指向同一个常量对象;如果没有则会创建对象(在堆空间),此时即使两次值是一样的,但是却创建了两个对象,此时equals为true,但==为false。
  • new对象:不管常量池有没有,直接创建对象。

访问修饰符

  • Protected:
    特别说明:
    其不同包的子类可以调用;但是不同包的其他类,就算创建了其子类对象,也无法直接调用。但是可以在子类中写方法,通过子类来调用。
    其实就是:被Protected修饰的成员,在子类中相当于子类的私有成员
    修饰符

String和Stringbuffer, StringBuilder。

Stringbuffer和StringBuilder类的对象可以被多次修改,而不会创建新的对象(节省内存)。而String中的对象是不可变的,因此可以理解为线程安全,但是每次修改都会创建新对象,对于内存紧张的情况并不适用。

  1. String(字符串常量):final修饰,不可被继承。
  2. Stringbuffer(字符串变量):final修饰,不可被继承。线程安全,方法基本都加了同步锁,效率低。
  3. StringBuilder(字符串变量):final修饰,不可被继承。线程不安全,效率高。

Final

final关键字可以理解为终态。

  1. final修饰类
      final修饰的类不允许被继承。一个类不能既是final的,又是abstract的。因为abstract的主要目的是定义一种约定,让子类去实现这种约定,而final表示该类不能被继承,两者矛盾。
  2. final修饰方法
      final修饰方法,表示该方法不能被子类中的方法覆写Override。
  3. final修饰变量
      final修饰的类属性和变量属性必须要进行显示初始化赋值。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。final成员变量表示常量,只能被赋值一次,赋值后值(地址指向)不再改变。
  4. 当函数的参数类型声明为final时,说明该参数是只读型的。传入的参数,如果是基本数据类型(值传递),就一直是这个值,不会改变;如果是引用数据类型(地址传递),就一直是这个地址,不会改变。这样使用可以避免误操作更改这个固定值。

static

  1. 成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。
  2. 静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据,不会随着对象的回收而销毁。
  3. 静态方法中不可以使用this或者super关键字。

垃圾回收机制(GC)

  • GC是垃圾收集的意思(Gabage Collection),Java提供的GC功能可以自动监测对象是否达到回收标准。
  • 因为垃圾回收器通常是作为一个单独的低级别的线程运行。
    • 会在cpu空闲的时候自动进行回收
    • 在堆内存存储满了之后
    • 主动调用System.gc()后尝试进行回收(不一定调用后马上会回收)
  • 强引用:就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用。垃圾收集器永远不会回收掉被强引用的对象
  • 软引用:用来描述一些还有用,但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
  • 弱引用:也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
  • 虚引用:也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用

抽象类、抽象方法、接口、枚举

  • abstract 关键字,和哪些关键字不能共存。
    • final:被final修饰的类不能有子类(不能被继承)。而被abstract修饰的类一定是一个父类(一定要被继承)。
    • private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法出现的就是需要被复写。
    • static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
  1. 抽象类:
    添加abstract修饰;
    如果一个类有抽象方法,那么这个类必须定义为抽象类;
    抽象类可以有抽象方法也可以有具体实现的方法;
    抽象类不能被实例化,只能被继承;
    继承一个抽象类,必须重写其中的抽象方法,否则也会被抽象化。
  2. 抽象方法:
    没有方法体{},必须使用abstract关键字来修饰;
    不能直接调用,只能被重写才能使用。
  3. 接口:
    接口可以理解为一种特殊的类(100%抽象类),里面全部是由全局常量和公共的抽象方法所组成。
    接口是解决Java无法使用多继承的一种手段
    接口中存在的变量一定是final,public,static的,这样可以更明确的限定这类事务的固有特性,不会发生改变。
  4. 枚举(enum):
    枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。
    公用的静态常量(public static final);
    jdk1.6以后switch语句开始支持枚举;
    enum

集合

  • 常用集合
    常用集合
    注意:红黑树就是平衡二叉树。
  • JDK1.8HashMap的更改
    • 数组+链表改成了数组+链表或红黑树(长度加到8转红黑树,长度减少到6转链表)
    • 为什么链表长度到8后要改为红黑树结构?为什么不一开始就用红黑树?
      • 因为链表的时间复杂度是n/2,红黑树是log(n),当n等于8时,从时间复杂度上考虑,红黑树效率更高;使用红黑树要有足够的空间,如果在数据量不大的时候,也就没有必要使用树结构。
    • 链表的插入方式从头插法改成了尾插法
    • 扩容的时候1.7需要对原数组中的元素进行重新hash定位在新数组的位置,1.8采用更简单的判断逻辑,位置不变或索引+旧容量大小
    • 在插入时,1.7先判断是否需要扩容,再插入,1.8先进行插入,插入完成再判断是否需要扩容;
  • 要在遍历的时候删除元素,使用Iterator(遍历器);要在遍历的时候添加元素,使用listIterator.add(),这是专门针对List集合遍历时的添加操作。
  • 集合排序(Comparable与Comparator)
    • Comparable:内部排序,实现该接口的类可自定义排序规则。
    • Comparator:外部排序,实现该接口可以定义一个针对某个类的排序方式。

反射

定义: 在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。

线程

  • ThreadLocal本地线程
  • wait和sleep的区别
    • sleep()是Thread类中的方法,而wait()则是Object类中的方法。
    • sleep()方法导致了程序暂停,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
    • wait()方法会导致线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
  • JDK自带线程池
    • newFixedThreadPool:创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
    • newCachedThreadPool:创建一个可缓存的线程池。这种类型的线程池特点是:
      • 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
      • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
    • newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。
    • newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。
    • 总结:
      • FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
      • CachedThreadPool的特点就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工作线程,从而释放工作线程所占用的资源。但是,但当出现新任务时,又要创建一新的工作线程,又要一定的系统开销。并且,在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值