Skip to content

Latest commit

 

History

History
226 lines (180 loc) · 18.6 KB

java面试基础P2.md

File metadata and controls

226 lines (180 loc) · 18.6 KB
1,实例变量、局部变量、类变量和final变量
  • 定义在类中的变量是类的成员变量,可以不进行初始化,Java会自动进行初始化,如果是 *引用类型 *默认初始化为 null ,如果是 基本类型 例如int则会默认初始化为0
  • 局部变量是定义在方法中的变量,必须要进行初始化,否则不同通过编译
  • 被static关键字修饰的变量是静态的,静态变量随着类的加载而加载,所以也被称为类变量
  • final修饰发变量是常量(详见4)

Tips:静态变量只能在类主体中定义,不能在方法中定义

2,构造函数

构造函数不能被继承,构造方法只能被显式或隐式的调用。

3,==、equals和hashcode()

“==”:作用是判断两个对象的地址是否相等, 即,判断两个对象是不是同一个对象,如果是基本数据类型,则比较的是值是否相等。 equal:作用是判断两个对象是否相等,但一般有两种使用情况 1.类没有覆盖 equals() 方法,则相当于通过“==”比较 2.类覆盖equals()方法,一般,我们都通过equals()方法来比较两个对象的内容是否相等,相等则返回true,如String

地址比较是通过计算对象的哈希值来比较的,==hashcode==属于Object的本地方法,对象相等(地址相等),hashcode相等,对象不相等,hashcode()可能相等,哈希冲突

4,final关键字

1.final修饰变量,则等同于==常量== 2.final修饰方法中的参数,称为==最终参数==。 3.final修饰,则类不能被继承 4.final修饰方法,则方法不能被重写。 5.final 不能修饰抽象类 6.final修饰的方法可以被重载不能被重写 7.final修饰变量,变量的引用(也就是指向的地址)不可变,但是引用的内容可以(地址中的内容可变)

5,finalize()图解

GC过程

6,String底层

String底层是char数组

7,线程状态转换图

在这里插入图片描述

8,内存分配策略

按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的。
静态存储分配是指在==编译时就能确定每个数据目标在运行时刻的存储空间需求==,因而在编译时就可以给他们分配固定的内存空间。这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求。
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的。和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存。和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例。==堆由大片的可利用块或空闲块组成==,堆中的内存可以按照任意顺序分配和释放。

9,JVM中的堆和栈

JVM是基于堆栈的虚拟机。JVM为每个新创建的线程都分配一个堆栈,也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
==java把内存分两种:一种是栈内存,另一种是堆内存==
(stack)与(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
栈(stack):是一个先进后出的数据结构,通常用于保存==方法(函数)中的参数,局部变量。== 堆 (heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),是一个运行时数据区,C中的malloc语句所产生的内存空间就在堆中。

10,堆和栈优缺点比较

栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢

11,不同数据类型存储

以下内容较长,但是看完以后就不会对java中的数据存储感到疑惑。

1,==基 本 类 型== 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。

这种类型的定义是通过诸如 int a = 3; long b = 255L; 的形式来定义的,称为自动变量。值得注意的是,==自动变量存的是字面值,不是类的实例==,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

int a = 3;
int b = 3

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

特别注意的是,这种==字面值的引用与类对象的引用不同==。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个==变化==。相反,通过字面值的引用来修改其值,==不会导致另一个指向此字面值的引用的值也跟着改变的情况==。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

字面值:在Java源代码中,字面值用于表示固定的值(fixed value)。数值型的字面值是最常见的,字符串字面值可以算是一种,当然也可以把特殊的null当做字面值。字面值大体上可以分为 整型字面值、浮点字面值、字符和字符串字面值、特殊字面值

2,==包装类数据== 如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据==全部存在于堆中==,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

String是一个特殊的包装类数据。即可以用

String str = new String(“abc”);

的形式来创建,也可以用

String str = “abc”;

的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过 ==new()== 的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = “abc”;中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有(详情看12)。

,String在内存中的存放

String是一个特殊的包装类数据,可以用用以下两种方式创建:

//第一种创建方式是用new()来新建对象的,它会存放于堆中。每调用一次就会创建一个新的对象。
String str = new String(“abc”);

String str = “abc”; 第二种创建方式先在栈中创建一个对String类的对象引用变量str,然后在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并返回o的地址,最后将str指向对象o的地址。

值得注意的是,一般String类中字符串值都是直接存值的。但像==String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!==

12,数组在内存中的存放
  • int x[] 或者int []x 时,在内存栈空间中创建一个数组引用,通过该数组名来引用数组。
  • x = new int[5] 将在堆内存中分配5个保存int型数据的空间,堆内存的首地址放到栈内存中,每个数组元素被初始化为0。
13,static变量在内存中的存放

用 static的修饰的变量和方法,实际上是指定了这些变量和方法在内存中的“固定位置”-static storage。既然要有“固定位置”那么他们的 “大小”似乎就是固定的了,有了固定位置和固定大小的特征了,在栈中或堆中开辟空间那就是非常的方便了。如果静态的变量或方法在不出其作用域的情况下,其引用句柄是不会发生改变的。

14,java中变量在内存中的分配

1、类变量(static修饰的变量)

在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期一直持续到整个”系统”关闭

2、实例变量

当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”。 实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存

3、局部变量

局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放.

TIPS:

栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 堆:存放用new产生的数据 静态域:存放在对象中用static定义的静态成员 常量池:存放常量 非RAM存储:硬盘等永久存储空间 类信息、常量、静态变量等数据储存在方法区

,hashmap的快速失败策略

快速失败(Fail-Fast)机制:对于==线程不安全==的集合对象的迭代器,如果在使用迭代器的过程中有其他线程修改了集合对象结构或者元素数量,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。 迭代 HashMap 采用快速失败机制,而 HashTable 不是,因为 HashTable 是线程安全的。

16,Object类的方法

1.clone方法 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

2.getClass方法 final方法,获得运行时类型。

3.toString方法 该方法用得比较多,一般子类都有覆盖。

4.finalize方法 该方法用于==释放资源==。因为无法确定该方法什么时候被调用,很少使用。

5.equals方法 该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

6.hashCode方法 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

7.wait方法 ==wait方法就是使当前线程等待该对象的锁==,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法

(2)其他线程调用了该对象的notifyAll方法

(3)其他线程调用了interrupt中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

8.notify方法 该方法唤醒在该对象上等待的某个线程。

9.notifyAll方法 该方法唤醒在该对象上等待的所有线程。

17,Java类的初始化顺序
  1. 静态代码块;
  2. 普通代码块;
  3. 构造函数。
18,本地方法和最终方法

本地方法:简单地讲,一个native Method就是一个==java调用非java代码的接口==;native方法表示该方法要用另外一种依赖平台的编程语言实现。 最终方法final void B(){},这样定义的方法就是最终方法,最终方法在子类中不可以被重写,也就是说,如果有个子类继承了这个最终方法所在的类,那么这个子类中不能出现void B(){}这样的方法。

19,优先级

淡云一笔安洛三福 单目>算数运算符>移位>比较>按位>逻辑>三目>赋值

20,sleep()、wait()、yield()、join()

1.sleep()方法

在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。 sleep()使当前线程进入阻塞状态,在指定时间内不会执行。

2.wait()方法

在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。

当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。

唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。

waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

3.yield方法

暂停当前正在执行的线程对象。

yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

yield()只能使同优先级或更高优先级的线程有执行的机会。

4.join方法 join()等待该线程终止。 等待调用join方法的线程结束,再继续执行。如:

t.join();	//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测

所谓的释放锁资源实际是通知对象内置的monitor对象进行释放,而只有所有对象都有内置的monitor对象才能实现任何对象的锁资源都可以释放。又因为所有类都继承自Object,所以wait()就成了Object方法,也就是通过wait()来通知对象内置的monitor对象释放,而且事实上因为这涉及对硬件底层的操作,所以wait()方法是native方法,底层是用C写的。 其他都是Thread所有,所以其他3个是没有资格释放资源的 而join()有资格释放资源其实是通过调用wait()来实现的


如有任何问题:请联系 18435138433@163.com
欢迎关注:自由的小黑
在这里插入图片描述