初出茅庐,Java小白寻求Offer序列
基本概念
Java语言有哪些优点?
- Java为纯面向对象语言,能够直接反应现实生活,使得编写程序更为容易。
- 平台无关性
- 提供了很多的内置类库,通过使用这些类库,简化了开发人员的程序设计工作,缩短了项目的开发时间。
- 提高了对Web应用开发的支持。
- 具有较好的安全性和健壮性。
如何实现在main方法执行前输出“hello world”
静态块
java程序初始化的顺序是怎么样
3个原则:
静态对象(变量)优先于非静态(变量)
父类优先于子类进行初始化
按照成员变量的定义顺序进行初始化
父类静态变量 父类静态代码块 子类静态变量 子类静态代码块 父类非静态变量 父类非静态代码块 父类构造函数 子类非静态变量 子类非静态代码块 子类构造函数
什么是构造函数
一种特殊的函数,用来在对象实例化时初始化对象的成员变量。在Java中,构造函数具有以下几个特点:
- 函数名必须和类名相同,并且不能有返回值
- 每个类可以有多个构造函数,当开发人员没有提供构造函数时,编译器在把源代码编译成字节码的过程中会提供一个没有参数的默认的构造函数。
- 构造函数总是伴随着new操作一起调用,且不能由开发者直接调用,必须由系统调用。在对象实例化之前自动被调用,且只运行一次。
- 构造函数可以被重载,不能被继承
- 子类可以通过super关键字来显式地调用父类的构造函数。
为什么Java中有些接口没有任何方法
标识接口,用来当做一个标识的作用,用来表明实现它的类是属于一个特定的类型。
Java中clone方法有什么作用
Java在处理基本数据类型时,采用的是值传递的方式执行,除此之外其他类型都是按照引用类型传递方式执行。对象除了在函数调用时是引用传递,在使用 = 赋值时采用的也是引用传递。
而在实际编程中,经常会遇到从某个已有的对象A创建出另一个与A具有相同状态的对象B,而对B的修改不会影响到A,这种情况下显然通过简单的赋值操作无法达到目的,而 Java 提供了一个简单有效的clone方法来满足这个需求。
什么是反射机制
反射机制是java语言中非常重要的特性,它允许程序在运行时进行自我检查,同时也允许对其内部的成员进行操作。
具体来说,反射机制提供的功能有:
- 得到一个对象所属的类
- 获取一个类的所有成员
- 获取一个类的所有成员变量和方法
- 在运行时创建对象
- 在运行时调用对象的方法
变量命名规则
在java语言中,变量名、函数名、数组名统称为标识符。
java规定,标识符只能由字母、数字、下划线和$符合组成,并且标识符第一个字符不能为数字。
break、continue、return
- break直接强行跳出当前循环
- continue用于停止当次循环,回到循环起始处
- return语句是一个跳转语句,用来表示从一个方法返回。
final、finally、finalize有什么区别
- final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖和类不可继承。
- finally作为异常处理的一部分,只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定被执行,经常被用在释放资源的情况下。
- finalize是Object类的一个方法,在垃圾回收器执行会调用被回收对象的finalize方法,可以覆盖此方法来实现对其资源的回收。
JDK中有哪些类是不可继承的
不能继承的类是使用了final关键字修饰的类。一般比较基本的类型为了防止扩展类无意间破坏原来方法的实现的类型都应该是final的,比如String、StringBuffer。
static关键字有哪些作用
- static成员变量:静态变量属于类,在内存中只有一个复制,主要静态变量所在的类被加载,这个静态变量就会被分配空间。
- static成员方法:方法属于类,不需要创建对象就可以使用
- static代码块:静态代码块,在类中独立于成员变量和成员方法的代码块。加载类时会执行static代码块,如果有多个按顺序执行,并且都执行一次。
- static内部类
使用switch时需要注意哪些事项
int 或者Integer
因为 byte、short和char类型可以隐式地转换为int类型,因此这些类型以及他们的包装类都可以作为switch的表达式。
java7中,switch开始支持String类型了,原理是通过String对象的hashCode方法得到一个int类型的hash值,然后用这个hash值来唯一标识case。
volatile有什么作用
在用java语言编写的程序中,为了提供程序的运行效率,编译器会把经常被访问的值存储在缓存中,读取时直接从缓存中读。在多线程编程时,变量的值可能会因为别的线程而改变了,缓存的值不会相应地改变,从而造成了读取的值和实际的值不一致的问题。
被volatile修饰的变量,程序每次用到它时都会直接从对应的内存中读取,而不会利用缓存,所以使用了volatile修饰成员变量后,所有线程在任何时候看到的变量的值都是相同的。
volatile不能保证操作的原子性,因此,一般情况下不能代替sychronized。此外,使用volatile会阻止编译器对代码进行优化,会降低程序的执行效率。
instanceof有什么作用
是一个二元运算符,判断一个引用类型的变量是否是一个类(或接口、抽象类、父类)的实例。
基本数据类型
Java中的基本数据类型有哪些?
只有8个:byte、short、int、long、float、double、char、boolean。
除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)。
引用类型有 类class 、接口 interface 、数组array。
int 和 Integer 有什么区别?
int是基本数据类型,Integer是int对应的包装类。
Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
类和对象
什么是面向对象
面向对象是当今软件开发方法的主流方法之一,它把数据及对数据的操作方法放在一起,作为一个相互依存的整体,即对象。对同类的对象抽象出其共性,即类,类中的大多数数据,只能被本类的方法进行处理。
什么是面向过程
一种以事件为中心的开发方法,自顶向下顺序执行,逐步求精,程序结构是按照功能划分为若干个基本模块。
面向对象的特征
- 继承:通过子类可以实现继承,子类继承父类的所有状态和行为,同时添加自身的状态和行为。
- 封装:将代码及处理数据绑定在一起的一种编程机制,该机制保证程序和数据不受外部干扰。
- 多态:包括重载和重写。
- 抽象:忽略一个主题中与当前目标无关的那些方法,以便更充分地注意与当前目标有关的方法。抽象包括两个方面:一是过程抽象,二是数据抽象。
面向对象开发优点
- 较高的开发效率
- 保证软件的鲁棒性
- 保证软件的高可维护性
什么是继承
继承是面向对象中一个非常重要的特性。通过继承,子类可以使用父类的一些成员变量和方法,提高代码的复用性,提高开发效率。
java中继承主要有以下几个特性
- 不支持多继承
- 子类只能继承父类的非私有(public和protected)成员变量和方法
- 同名的子类成员变量会覆盖父类的成员变量
- 相同的函数签名(相同的方法名,相同的参数个数和类型),子类会覆盖父类的方法
组合和继承有什么区别
- 组合是指在新类里面创建原有类的对象,重复利用已有类的功能。
- 继承是面向对象的主要特性之一,允许设计人员根据其他类的实现来定义一个类的实现。
- 继承关系: is - a关系 组合关系:has - a 关系
- 能使用组合就不要使用继承
重载和覆盖有什么区别
- 覆盖是子类和父类之间的关系,重载是同一个类中方法之间的关系
- 覆盖只能有一个方法或只能由一对方法产生关系,重载是多个方法之间的关系
- 覆盖要求参数列表相同,重载要求参数列表不同
this和super有什么区别
- this用来指向当前实例对象
- super可以用来访问父类的方法和成员变量
多态实现的机制是什么
编译时多态和运行时多态,编译时多态是通过方法的重载实现,运行时多态是通过方法的覆盖来实现。
重装和重写的区别?
- 重载为编译时多态,重写是运行时多态。
- 重载必须是同类中名称相同参数不同(包括个数不同和类型不同),返回类型不同不构成重载。
- 重写发生于子类对父类的覆盖,子类继承父类方法名相同、参数列表相同、返回类型相同才构成重写。
重写equals为何也要重写hashCode?
equals方法和hashCode方法都是Object类的方法,而在Java中所有对象默认继承Object类。
hashCode的作用是为任何对象生成一个哈希码,逻辑上相同的对象生成相同的哈希码。在Java中内置求出的哈希码是基于存储地址的一个映射关系,保证对象的哈希码的一一对应关系。
在JVM中,要插入一个对象,必须先比较对象的哈希码,如果相同,再比较equal方法是否为true,如果为true表明已经存在该对象,不插入。如果不为true则按照哈希冲突的原则插入到其他地方。
所以如果重写了equals方法不重写hashCode方法,那么hashCode不相等就没机会比较equals方法(也就是没效果)
抽象类和接口的区别,有哪些使用场景呢?
- 抽象类可以有自己的数据成员,也可以有非抽象的成员方法。接口中只能有静态的不能修改的数据成员(static final),只能有抽象的成员方法。
- 实现抽象类和接口的类都必须实现其他的所有抽象方法。
除了使用new关键字创建对象意外,试列举另外三种以上创建实例的方式
- 克隆
- 反射
- new
- 反序列化
获得一个类的类对象有哪些方式?
- 类型.Class,例如:String.Class
- 对象.getClass(),例如:”hello”.getClass()
- Class.forName(),例如:Class.forName(“java.lang.String”)
如何通过反射创建对象?
通过类对象调用 newInstance() 方法。
1
String.class.newInstance()
通过类对象的 getConstructor() 或 getDeclaredConstructor() 方法获得构造器(Constructor)对象并调用其 newInstance() 方法创建对象。
1
String.class.getConstructor(String.class).newInstance("Hello");
final关键字的意义是什么?修饰变量、对象、方法和类时有何不同
- 意义:最终的,不可改变的。
- 不同:修饰变量,为常量,值不可变;修饰对象,值可变,引用不变;修饰方法,方法不可重写; 修饰类,无子类,不可以被继承,更不可能被重写。
一个”.java”源文件中是否可以包括多个类(不是内部类)?有什么限制?
- 这个是可以的,一个“.java”源文件里面可以包含多个类,但是只允许有一个public类,并且类名必须和文件名一致。
- 每个编译单元只能有一个public 类(不包含内部类,内部类可加public)。这么做的意思是,每个编译单元只能有一个公开的接口,而这个接口就由其public 类来表示。
字符串
JAVA中String类与StringBuffer类的区别
- String类
该类一旦产生一个字符串,其对象就不可变。String类的内容和长度是固定的。如果程序需要获得字符串的信息需要调用系统提供的各种字符串操作方法实现。虽然通过各种系统方法可以对字符串施加操作,但这并不改变对象实例本身,而是生成一个新的实例。系统为String类对象分配内存,是按照对象所包含的实际字符数分配的。
- StringBuffer类
该类处理可变的字符串。如果要修改一个StringBuffer类的字符串,不需要再创建新的字符串对象,而是直接操作原来的串。该类的各种字符串操作方法与String类提供的方法不相同。系统为StringBuffer类分配内存时,除去当前字符所占的空间外,还提供另外的16个字符大小的缓冲区。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
集合
Java中的HashCode(1)之hash算法基本原理?
Java中的集合有两类,一类是List,一类是Set。List内的元素是有序的,元素可以重复。Set元素无序,但元素不可重复。要想保证元素不重复,两个元素是否重复应该依据什么来判断呢?用Object.equals方法。但若每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说若集合中已有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。于是Java采用了哈希表的原理。
当Set接收一个元素时根据该对象的内存地址算出hashCode,看它属于哪一个区间,在这个区间里调用equeals方法。
确实提高了效率。但一个面临问题:若两个对象equals相等,但不在一个区间,根本没有机会进行比较,会被认为是不同的对象。所以Java对于eqauls方法和hashCode方法是这样规定的:
1 如果两个对象相同,那么它们的hashCode值一定要相同。也告诉我们重写equals方法,一定要重写hashCode方法。
2 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用eqauls方法比较。
ArrayList和LinkedList、Vector的区别?
先比较ArrayList和LinkedList。
相同点都是实现了集合顶层接口Collectio接口下的List接口。
不同点是ArrayList是实现基于动态数组的数据结构,而LinkedList是实现基于链表的数据结构(双链表),所以他们的区别就是数组和链表的区别。这里就复习下数组和链表的区别吧!
数组:随机访问快于链表。
链表:新增和删除操作快于数组。
LinkedList同时实现了队列Query接口。
再比较ArrayList和Vector。
Vector和ArrayList很类似,不同的是Vector属于强同步类,即线程安全,也因此开销就比ArrayList要大。正常情况下,我们都是使用ArrayList而不是Vector,因为同步完全可以由我们自己来控制。
HashMap、Hashtable、TreeMap、WeakHashMap有哪些区别?
HashMap是Hashtable的轻量级实现(非线程安全),都实现了Map接口。HashMap允许空键值(最多一条),而Hashtable不允许。
TreeMap实现了SortMap接口,能够把保存的记录根据键来排序,因此取出来的是排序后的键值对,如果需要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
WeakHashMap与HashMap类似,不同在于WeakHashMap是弱引用的方式,只要WeakHashMap中的可以不再被外部引用,它就被垃圾回收器回收。而HashMap中的key被删除后,才会被垃圾回收器回收。
Collection接口和Collections类的区别
Collection
是集合类的上层接口。本身是一个Interface,里面包含了一些集合的基本操作。
Collection接口也是 Se t接口和 List 接口的父接口
Collections
Collections是一个集合框架的帮助类,里面包含一些对集合的排序,搜索以及序列化的操作。
IO流
Java中有几种类型的流?
- 字节流和字符流。
- 字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。
在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。
序列化的原理和作用
- 序列化是将数据结构或对象转换成二进制串的过程,简单点说序列化就是就是将数据分解成字节流,以便存储在文件中或在网络上传输的过程。
- 反序列化就是打开字节流并重构对象。
- Java中实现序列化只要让类实现 Serializable 接口即可。
序列化的特点
如果一个类能被序列化,那么它的子类也能够被序列化
由于static(静态)代表类的成员,transient(Java语言关键字,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。)代表对象的临时数据,因此被声明为这两种类型的数据成员不被序列化。
异常
java error和exception的区别,RuntimeException和非RuntimeException的区别
Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。
Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
Exception又分为RuntimeException(运行时异常),非RuntimeException(受检查异常)。
运行时异常,表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。
受检查异常,是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之为受检查异常。
列出一些你常见的运行时异常?
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- IndexOutOfBoundsException (下标越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?
会执行,在方法返回调用者前执行。
阐述final、finally、finalize的区别。
- final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。
- finally:通常放在 try…catch… 的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在 finally 块中。
- finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。
线程
Java线程的5种状态及切换(透彻讲解)
新建(NEW):
可运行(RUNNABLE)
运行(RUNNING)
阻塞(BLOCKED)
死亡(DEAD)
如何创建线程?
- 第一种直接new Thread 。
- 第二种new 一个实现Runnable接口的实现类。
- 第三种,通过线程池来管理创建等。
方法内部,如何实现更好的异步?
更好的实现异步,那就是说我们在方法内部避免频繁的new 线程,就可以考虑线程池了。 那么线程池如何创建? 这里可以new 一个线程池,但是需要考虑单例,或者在程序初始启东时,就创建一个线程池,让他跑着,然后在具体方法的时候,通过线程池来创建线程,实现异步。
Thread类的 sleep() 方法和对象的 wait() 方法都可以让线程暂停执行,它们有什么区别?
- sleep() 方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。
- wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
线程的 sleep() 方法和 yield() 方法有什么区别?
- sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield() 方法只会给相同优先级或更高优先级的线程以运行的机会;
- 线程执行 sleep() 方法后转入阻塞(blocked)状态,而执行 yield() 方法后转入就绪(ready)状态;
- sleep()方法声明抛出 InterruptedException,而 yield() 方法没有声明任何异常;
- sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
- 不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。
- 因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
请说出与线程同步以及线程调度相关的方法。
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
synchronized关键字的用法?
ynchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。
举例说明同步和异步。
- 如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。
简述synchronized 和java.util.concurrent.locks.Lock的异同?
Lock是Java 5以后引入的新的API,和关键字synchronized相比。
主要相同点:
Lock 能完成synchronized所实现的所有功能;
主要不同点:
用法不一样:synchronized既可以加在方法上,也可以加载特定的代码块中,括号中表示需要锁的对象。而lock需要显式地指定起始位置和终止位置。
性能不一样:在竞争不是很激烈的情况下,synchronized的性能比较好,在竞争很激烈的情况下,synchronized的性能会下降地非常快,而lock基本保持不变。
Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。
锁机制不一样:synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)。
什么是守护线程
java提供了两种线程:守护线程和用户线程。
守护线程是指
JDBC
阐述JDBC操作数据库的步骤。
1.加载驱动。
2.创建连接。
3.创建语句。
4.执行语句。
5.处理结果。
6.关闭资源。
Statement和PreparedStatement有什么区别?哪个性能更好?
PreparedStatement与Statement相比:
- PreparedStatement接口代表预编译的语句,它主要的优势在于可以减少SQL的编译错误并增加SQL的安全性(减少SQL注射攻击的可能性);
- PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接SQL语句的麻烦和不安全;
- 当批量处理SQL或频繁执行相同的查询时,PreparedStatement有明显的性能上的优势,由于数据库可以将编译优化后的SQL语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。
使用JDBC操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?
- 要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的setFetchSize()方法指定每次抓取的记录数(典型的空间换时间策略);
- 要提升更新数据的性能可以使用PreparedStatement语句构建批处理,将若干SQL语句置于一个批处理中执行。
在进行数据库编程时,连接池有什么作用?
- 由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。
池化技术在Java开发中是很常见的,在使用线程时创建线程池的道理与此相同。
基于Java的开源数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等。
JDBC中如何进行事务处理?
- Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。
- 除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。
JDBC能否处理Blob和Clob?
- Blob是指二进制大对象(Binary Large Object),而Clob是指大字符对象(Character Large Objec),因此其中Blob是为存储大的二进制数据而设计的,而Clob是为存储大的文本数据而设计的。
- JDBC的PreparedStatement和ResultSet都提供了相应的方法来支持Blob和Clob操作。
Java平台和内存管理
为什么说java是平台独立性语言?
平台的独立性是指可以在一个平台上编写和编译程序,而在其他平台上运行。保证Java具有平台独立性的机制为“中间码”和“Java虚拟机”。
Java编译后生成“中间码”,不同的硬件平台都会安装有不同的JVM,由JVM来负责把“中间码”翻译为硬件平台能执行的代码。
“中间码”也称字节码
JVM加载class文件的原理机制是什么
java是一种动态的解释型语言,类只有加载到JVM中才能运行。
当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成一个完整的Java应用程序。这个加载过程由类加载器来完成,具体来说就是由ClassLoader和他的子类来实现的。类加载器本身也是一个类,实质是把类文件从硬盘读取到内存中。
类的加载方式分为隐式加载和显式加载。前者通过new等方式创建对象时加载对应的类到JVM中,后者通过class.forName方法来把所需的类加载到JVM中。
类的加载是动态的,不会一次性将所有类全部加载后再运行,而是先加载保证程序运行的基础类。其他类会在需要的时候再加载。
java中将类分为三种,系统类,扩展类和自定义类。Java针对这三种不同的类提供了三种类型的加载器。
Bootstrap Loader 负责加载系统类(jre/lib/rt.jar的类)
ExtClassLoader 负载加载扩展类(jar/lib/ext/*.jar的类)
AppClassLoader 负责加载应用类(classpath指定目录或jar中的类)
什么是GC
垃圾回收的意思。主要作用是回收程序中不再使用的内存。
主要负责3项任务:分配内存,确保被引用的对象不被错误地回收、回收不再被引用的对象的内存空间。
对对象而言,如果没有任何变量去引用它,那么该对象将不可能被程序访问,因此可以认为是垃圾信息,可以被回收。
对垃圾回收器来说,它使用有向图来记录和管理内存中的所有对象,通过这个有向图就可以识别哪些对象是可达的(有引用变量引用它就是可达的)哪些对象是不可达的,所有不可达的对象就会被垃圾回收。
常用的垃圾回收算法
java是否存在内存泄漏问题
内存泄漏是指一个不再被程序使用的对象或者变量还在内存中占有存储空间。
在Java中判断一个内存空间是否符合垃圾回收的标准有两个:
给对象赋予了null,以后没有使用过。
给对象赋予了新值,重新分配了内存空间。
一般来讲,内存泄漏主要有两种情况:一是在堆中申请的空间没有被释放,而是对象已经不在被使用,但仍然在内存中留着。垃圾回收机制的引入可以在有效地解决第一件情况;而对于第二种情况,垃圾回收机制则无法保证不再使用的对象会被释放。因此,Java语言中的内存泄漏主要指第二种情况。
在Java语言中,容易引起内存泄漏的原因很多,主要有以下几个方法:
静态集合类、各种连接、监听器、变量不合理的作用域
Java中堆和栈有什么区别
在Java语言,堆和栈都是内存存放数据的地方。基本数据类型的变量和对象的引用变量,都分配在栈上。
引用类型的变量,其内存分配在堆上或者常量池(例如字符串常量和基本数据类型常量)中,通过new等方式创建。
具体而言,栈内存主要存放基本数据类型变量和引用变量。栈内存的管理是通过压栈和弹栈来完成,以栈帧为基本单位来管理程序的调用关系。
堆内存用来存放运行时创建的对象。JVM是基于堆栈的虚拟机,每个Java程序都运行在一个单独的JVM中,都对应一个堆内存,因此在多线程情况下,线程之间共享堆内存,所以多线程在访问堆中数据时需要进行同步。
在Java中,堆内存的垃圾回收由垃圾回收器自动回收。
感谢
整理本文时参考了以下文章,非常感谢大佬们的分享: