抽象、继承、封装、多态性java
修饰符 | 当前类 | 同包 | 子类 | 其它包 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
相同点:程序员
&和&&均可以用做逻辑与的运算符,表示逻辑与(and)。编程
不一样点:数组
&&具备短路的功能,而&不具有短路功能。浏览器
当&运算符两边的表达式的结果都为true时,整个运算结果才为true。而&&运算符第一个表达式为false时,则结果为false,再也不计算第二个表达式。安全
&还能够用做位运算符,当&操做符两边的表达式不是boolean类型时,&表示按位与操做,咱们一般使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如:0x31 & 0x0f的结果为0x01。服务器
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11cookie
2 << 3(左移3位至关于乘以2的3次方,右移3位至关于除以2的3次方)网络
数组没有length()方法,有length的属性。String有length()方法。session
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
String 类是final类,不能够被继承。
两个对象,一个是静态区的"xyz",一个是用new建立在堆上的对象。
不对,若是两个对象x和y知足x.equals(y) == true,它们的哈希码(hash code)应当相同。
1)功能不一样 "=="是判断两个变量或实例是否是指向同一个内存空间。 "equals"是判断两个变量或实例所指向的内存空间的值是否是相同。
2)定义不一样 "equals"在JAVA中是一个方法。 "=="在JAVA中只是一个运算符合。 例子: Student student1 = new Student()...
3)运行速度不一样 "=="比"equals"运行速度快,由于"=="只是比较引用。
不对,两个对象的 hashCode()相同,equals()不必定 true。
String和StringBuffer/StringBuilder,它们能够储存和操做字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的,而StringBuffer/StringBuilder类表示的字符串对象能够直接进行修改。
StringBuilder是Java 5中引入的,它和StringBuffer的方法彻底相同,区别在于它是在单线程环境下使用的,由于它的全部方面都没有被synchronized修饰(非同步),所以它的效率也比StringBuffer要高。
indexOf()
:返回指定字符的索引。
charAt()
:返回指定索引处的字符。
replace()
:字符串替换。
trim()
:去除字符串两端空白。
split()
:分割字符串,返回一个分割后的字符串数组。
getBytes()
:返回字符串的 byte 类型数组。
length()
:返回字符串长度。
toLowerCase()
:将字符串转成小写字母。
toUpperCase()
:将字符串转成大写字符。
substring()
:截取字符串。
equals()
:字符串比较。
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,然后者实现的是运行时的多态性。重载发生在一个类中,同名的方法若是有不一样的参数列表(参数类型不一样、参数个数不一样或者两者都不一样)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型
构造器不能被继承,所以不能被重写,但能够被重载。
静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类无论建立多少个对象,静态变量在内存中有且仅有一个拷贝。
实例变量必须依存于某一实例,须要先建立对象而后经过对象才能访问到它。静态变量能够实现让多个对象共享内存。
有两种方式:
1)实现Cloneable接口并重写Object类中的clone()方法。
2)实现Serializable接口,经过对象的序列化和反序列化实现克隆,能够实现真正的深度克隆。
一个内部类对象能够访问建立它的外部类对象的成员,包括私有成员。
普通类不能包含抽象方法,抽象类能够包含抽象方法。
抽象类不能直接实例化,普通类能够直接实例化。
不能,定义抽象类就是让其余类继承的,若是定义为 final 该类就不能被继承,这样彼此就会产生矛盾,因此 final 不能修饰抽象类。
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类能够有构造函数;接口不能有。
main 方法:抽象类能够有 main 方法,而且咱们能运行它;接口不能有 main 方法。
实现数量:类能够实现不少个接口;可是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法能够是任意访问修饰符。
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化以后值就不能被修改。
final
:修饰符(关键字)有三种用法:若是一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,所以它和abstract是反义词。将变量声明为final,能够保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在之后的引用中只能读取不可修改。被声明为final的方法也一样只能使用,不能在子类中被重写。
finally
:一般放在try…catch…的后面构造老是执行代码块,这就意味着程序不管正常执行仍是发生异常,这里的代码只要JVM不关闭都能执行,能够将释放外部资源的代码写在finally块中。
finalize
:Object类中定义的方法,Java中容许使用finalize()方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。这个方法是由垃圾收集器在销毁对象时调用的,经过重写finalize()方法能够整理系统资源或者执行其余清理工做。
所谓多态,指的就是父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。多态的实现的关键在于“动态绑定”。
clone()
,equals()
,hashCode()
,toString()
,notify()
,notifyAll()
,wait()
,finalize()
,getClass()
泛型即参数化类型,在建立集合时,指定集合元素的类型,此集合只能传入该类型的参数。类型擦除:java编译器生成的字节码不包含泛型信息,因此在编译时擦除:1.泛型用最顶级父类替换;2.移除。
并发编程中:原子性问题,可见性问题,有序性问题。
volatile
关键字能保证可见性,字能禁止指令重排序,可是不能保证原子性。可见性只能保证每次读取的是最新的值,可是volatile没办法保证对变量的操做的原子性。在生成的会变语句中加入Lock关键字和内存屏障。
Lock
实现提供了比使用synchronized 方法和语句可得到的更普遍的锁定操做,它能以更优雅的方式处理线程同步问题。用sychronized修饰的方法或者语句块在代码执行完以后锁自动释放,而用Lock须要咱们手动释放锁。
能,Java 中能够建立 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。个人意思是,若是改变引用指向的数组,将会受到 volatile 的保护,可是若是多个线程同时改变数组的元素,volatile 标示符就不能起到以前的保护做用了
集合,线性结构(数组,队列,链表和栈),树形结构,图状结构。
Java 中的 TreeMap 是使用红黑树实现的。
poll()
和remove()
都是从队列中取出一个元素,可是poll()
在获取元素失败的时候会返回空,可是remove()
失败的时候会抛出异常。
在Java 6中Arrays.sort()
和Collections.sort()
使用的是MergeSort
,而在Java 7中,内部实现换成了TimSort
,其对对象间比较的实现要求更加严格。
HashMap是数组+链表+红黑树(JDK1.8增长了红黑树部分)实现的。
HashMap最多只容许一条记录的键为null,容许多条记录的值为null。
HashMap非线程安全。ConcurrentHashMap线程安全。
解决碰撞:当出现冲突时,运用拉链法,将相同的结点连接在一个单链表中,散列表长m,则定义一个由m个头指针组成的指针数组T,地址为i的结点插入以T(i)为头指针的单链表中。Java8中,冲突的元素超过限制(8),用红黑树替换链表。
Vector属于线程安全级别的,可是大多数状况下不使用Vector,由于线程安全须要更大的系统开销。
1) 历史缘由: Hashtable继承Dictonary类, HashMap继承自abstractMap
2) HashMap容许空的键值对, 但最多只有一个空对象,而HashTable不容许。
3) HashTable同步,而HashMap非同步,效率上比HashTable要高
在多线程环境下,使用HashMap进行put操做会引发死循环,致使CPU利用率接近100%,因此在并发状况下不能使用HashMap。
HashTable使用synchronized来保证线程的安全,可是在线程竞争激烈的状况下HashTable的效率很是低下。当一个线程访问HashTable的同步方法,其余方法访问HashTable的同步方法时,会进入阻塞或者轮询状态。若是线程1使用put进行元素添加,线程2不但不能用put方法添加于元素同是也没法用get方法来获取元素,因此竞争越激烈效率越低。
HashTable容器在竞争激烈的并发环境效率低下的缘由是全部访问HashTable的线程都必须竞争同一把锁,假如容器有多把锁,每一把锁用于锁住容器中一部分数据,那么多线程访问容器里不一样数据段的数据时,线程间就不会存在锁竞争,从而能够有效提升并发访问率,这就是ConcurrentHashMap的锁分段技术。将数据分红一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据的时候,其余段的数据也能被其余线程访问。
ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问。
ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
LinkedList是双向链表
Comparable 接口用于定义对象的天然顺序,是排序接口,而 comparator 一般用于定义用户定制的顺序,是比较接口。咱们若是须要控制某个类的次序,而该类自己不支持排序(即没有实现Comparable接口),那么咱们就能够创建一个“该类的比较器”来进行排序。Comparable 老是只有一个,可是能够有多个 comparator 来定义对象的顺序。
Collection是Java集合框架中的基本接口。
Collections是Java集合框架提供的一个工具类,其中包含了大量用于操做或返回集合的静态方法。
List,Set都是继承自Collection接口
List特色:元素有放入顺序,元素可重复
Set特色:元素无放入顺序,元素不可重复,重复元素会覆盖掉
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引发元素位置改变。
List:和数组相似,List能够动态增加,查找元素效率高,插入删除元素效率低,由于会引发其余元素位置改变。
Arraylist:
优势:ArrayList是实现了基于动态数组的数据结构,由于地址连续,一旦数据存储好了,查询操做效率会比较高(在内存里是连着放的)。
缺点:由于地址连续, ArrayList要移动数据,因此插入和删除操做效率比较低。
LinkedList:
优势:LinkedList基于链表的数据结构,地址是任意的,因此在开辟内存空间的时候不须要等一个连续的地址,对于新增和删除操做add和remove,LinedList比较占优点。LinkedList 适用于要头尾操做或插入指定位置的场景。
缺点:由于LinkedList要移动指针,因此查询操做性能比较低。
适用场景分析:
当须要对数据进行对此访问的状况下选用ArrayList,当须要对数据进行屡次增长删除修改时采用LinkedList。
一、sleep()方法
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操做受到系统计时器和调度程序精度和准确性的影响。 让其余线程有机会继续执行,但它并不释放对象锁。也就是若是有Synchronized同步块,其余线程仍然不能访问共享数据。注意该方法要捕获异常
好比有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另外一个为MIN_PRIORITY,若是没有Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。
总之,sleep()可使低优先级的线程获得执行的机会,固然也可让同优先级、高优先级的线程有执行的机会。
二、yield()方法
yield()方法和sleep()方法相似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行,另外yield()方法只能使同优先级或者高优先级的线程获得执行机会,这也和sleep()方法不一样。
三、join()方法
Thread的非静态方法join()让一个线程B“加入”到另一个线程A的尾部。在A执行完毕以前,B不能工做。
Thread t = new MyThread(); t.start(); t.join();
保证当前线程中止执行,直到该线程所加入的线程完成为止。然而,若是它加入的线程没有存活,则当前线程不须要中止。
虽然二者都是用来暂停当前运行的线程,可是 sleep() 实际上只是短暂停顿,由于它不会释放锁,而 wait() 意味着条件等待,这就是为何该方法要释放锁,由于只有这样,其余等待的线程才能在知足条件时获取到该锁。
1)继承Thread类,重写run函数
2)实现Runnable接口,重写run函数
3)实现Callable接口,重写call函数
Java的线程是经过java.lang.Thread类来实现的。VM启动时会有一个由主方法所定义的线程。能够经过建立Thread的实例来建立新的线程。每一个线程都是经过某个特定Thread对象所对应的方法run()来完成其操做的,方法run()称为线程体。经过调用Thread类的start()方法来启动一个线程。
进程值运行中的程序(独立性,动态性,并发性),线程指进程中的顺序执行流。区别是:1.进程间不共享内存 2.建立进程进行资源分配的代价要大得多,因此多线程在高并发环境中效率高。
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其余线程不能进行访问直到该线程读取完,其余线程才可以使用。不会出现数据不一致或者数据污染。
新建状态:使用 new 关键字和 Thread 类或其子类创建一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:当线程对象调用了start()方法以后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:若是就绪状态的线程获取 CPU 资源,就能够执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它能够变为阻塞状态、就绪状态和死亡状态。
阻塞状态:若是一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源以后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或得到设备资源后能够从新进入就绪状态。能够分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized同步锁失败(由于同步锁被其余线程占用)。
其余阻塞:经过调用线程的 sleep() 或 join() 发出了 I/O请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程从新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其余终止条件发生时,该线程就切换到终止状态。
可重入性:
ReenTrantLock和synchronized使用的锁都是可重入的,二者都是同一个线程每进入一次,锁的计数器都自增1,因此等到锁的计数器降低为0时才能释放锁。
锁的实现:
synchronized是依赖JVM实现的,ReenTrantLock是JDK实现的,相似于操做系统控制实现和用户本身写代码实现。
性能的区别:
synchronized在JDK5优化后,二者性能差很少了。若是两种方法均可以使用的状况下,官方建议使用synchronized,由于使用便利。
功能的区别:
synchronized由编译器加锁和释放,默认是非公平锁,ReenTrantLock手动加锁和释放锁,若是忘记释放容易引发死锁,可是对于粒度控制强于synchronized关键字。
ReenTrantLock能够指定是公平锁仍是非公平锁,synchronized只能是非公平锁,所谓的公平锁就是先等待的线程先获取锁。
ReenTrantLock提供了中断锁和等待锁的功能,经过lock.lockInterruptibly()实现中断锁,经过lock.tryLock()实现等待锁。
ReenTrantLock提供了一个Condition类,实现了线程之间的通讯。
同步:调用方须要主动等待结果的返回。
异步:不须要主动等待结果的返回,而是经过其余手段,好比状态通知,回调函数等。
阻塞:是指结果返回以前,当前线程被挂起,不作任何事。
非阻塞:是指结果在返回以前,线程能够作一些其余事,不会被挂起。
一、在主函数中使用join()方法
t1.start(); t2.start(); t3.start(); t1.join();//不会致使t1和t2和t3的顺序执行 t2.join(); t3.join(); System.out.println("Main finished");
二、使用CountDownLatch
public class WithLatch{ public static void main(String[] args){ CountDownLatch latch = new CountDownLatch(3); for(int i=0;i<3;i++){ new ChildThread(i,latch).start(); } try{ latch.await(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Main finished"); } static calss ChildThread extends Thread{ private int id = -1; private CountDownLatch latch = null; public ChildThread(int id, CountDownLatch latch){ this.id = id; this.latch = latch; } public void run(){ try{ Thread.sleep(Math.abs(new Random().nextInt(5000))); System.out.println(String.format("Child Thread %d finished",id)); }catch(InterruptedExcepion e){ e.printStackTrace(); }finally{ latch.countDown(); } } } }
三、使用线程池
public class WithExecutor{ public static void main(String[] args) throws InterruptedExcepion{ ExecutorService pool = Executors.newFixedThreadPool(3); List<Callable<Void>> list = new ArrayList<Callable<Void>>(); for(int i=0;i<3;i++){ list.add(new ChildThread(i)); } try{ pool.invokeAll(list); }finally{ pool.shutdown(); } System.out.println("Main finished"); } static class ChildThread implements Callable<Void>{ private int id = -1; public ChildThread (int id){ this.id = id; } public Void call() throws Exception{ try{ Thread.sleep(Math.abs(new Random().nextInt(5000))); System.out.println(String.format("Child Thread %d finished",id)); }catch(InterruptedException e){ e.printStackTrace(); } return null; } } }
因为在平时的工做中,线上服务器是分布式多台部署的,常常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题。
同步和异步最大的区别就在于。一个须要等待,一个不须要等待。同步能够避免出现死锁,读脏数据的发生,通常共享某一资源的时候用,若是每一个人都有修改权限,同时修改一个文件,有可能使一我的读取另外一我的已经删除的内容,就会出错,同步就会按顺序来修改。
Error表示系统级的错误和程序没必要处理的异常,是很难恢复的一种严重问题;好比内存溢出,不可能期望程序能处理这样的状况;
Exception表示须要捕捉或者须要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示若是程序运行正常,从不会发生的状况。
会执行,在方法返回调用者前执行。
异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题一般就不会发生。
受检异常跟程序运行的上下文环境有关,即便程序设计无误,仍然可能因使用的问题而引起。Java编译器要求方法必须声明抛出可能发生的受检异常,可是并不要求必须声明抛出未被捕获的运行时异常。
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
BIO:Block IO 同步阻塞式 IO,就是咱们日常使用的传统 IO,它的特色是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端经过 Channel(通道)通信,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操做基于事件和回调机制。
1.OSI模型把网络通讯的工做分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。每一层对于上一层来说是透明的,上层只须要使用下层提供的接口,并不关心下层是如何实现的。
2.TCP/IP参考模型是首先由ARPANET所使用的网络体系结构。这个体系结构在它的两个主要协议出现之后被称为TCP/IP参考模型(TCP/IP Reference Model)。这一网络协议共分为四层:网络访问层、互联网层、传输层和应用层。
3.TCP/IP模型的分层及与OSI参考模型的对应关系为:
网络访问层--对应OSI参考模型的物理层和数据链路层;
网络层--对应OSI参考模型的网络层;
传输层--对应OSI参考模型的传输层;
应用层--对应OSI参考模型的会话层、表示层和应用层。
在TCP的链接中,数据流必须以正确的顺序送达对方。TCP的可靠性是经过顺序编号和确认(ACK)来实现的。
TCP 链接是经过三次握手进行初始化的。三次握手的目的是同步链接双方的序列号和确认号并交换 TCP 窗口大小信息。
第一次是客户端发起链接;第二次表示服务器收到了客户端的请求;第三次表示客户端收到了服务器的反馈。
TCP(Tranfer Control Protocol)的缩写,是一种面向链接的保证传输的协议,在传输数据流前,双方会先创建一条虚拟的通讯道。能够不多差错传输数据。
UDP(User DataGram Protocol)的缩写,是一种无链接的协议,使用UDP传输数据时,每一个数据段都是一个独立的信息,包括完整的源地址和目的地,在网络上以任何可能的 路径传到目的地,所以,可否到达目的地,以及到达目的地的时间和内容的完整性都不能保证。
因此TCP比UDP多了创建链接的时间。相对UDP而言,TCP具备更高的安全性和可靠性。
TCP协议传输的大小不限制,一旦链接被创建,双方能够按照必定的格式传输大量的数据,而UDP是一个不可靠的协议,大小有限制,每次不能超过64K。
cookie 是 Web 服务器发送给浏览器的一块信息。浏览器会在本地文件中给每个 Web 服务器存储 cookie。之后浏览器在给特定的 Web 服务器发请求的时候,同时会发送全部为该服务器存储的 cookie。
不管客户端浏览器作怎么样的设置,session都应该能正常工做。客户端能够选择禁用 cookie,可是, session 仍然是可以工做的,由于客户端没法禁用服务端的 session。
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来讲 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了不少 java 程序调试和分析的工具。简单来讲:若是你须要运行 java 程序,只需安装 JRE 就能够了,若是你须要编写 java 程序,须要安装 JDK。
Java中的全部类,必须被装载到jvm中才能运行,这个装载工做是由jvm中的类装载器完成的,类装载器所作的工做实质是把类文件从硬盘读取到内存中
1).系统类
2).扩展类
3).由程序员自定义的类
1).隐式装载, 程序在运行过程当中当碰到经过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中
2).显式装载, 经过class.forname()等方法,显式加载须要的类
一个应用程序老是由n多个类组成,Java程序启动时,并非一次把全部的类所有加载后再
运行,它老是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,由于java最先就是为嵌入式系统而设计的,内存宝贵,这是一种能够理解的机制,而用到时再加载这也是java动态性的一种体现
Java中的类装载器实质上也是类,功能是把类载入jvm中,值得注意的是jvm的类装载器并非一个,而是三个,层次结构以下:
Bootstrap Loader - 负责加载系统类
ExtClassLoader - 负责加载扩展类
AppClassLoader - 负责加载应用类
为何要有三个类加载器,一方面是分工,各自负责各自的区块,另外一方面为了实现委托模型,下面会谈到该模型
1) Bootstrap类加载器 – JRE/lib/rt.jar
2) Extension类加载器 – JRE/lib/ext或者java.ext.dirs指向的目录
3) Application类加载器 – CLASSPATH环境变量, 由-classpath或-cp选项定义,或者是JAR中的Manifest的classpath属性定义.
类加载器的工做原理基于三个机制:委托、可见性和单一性
当一个类加载和初始化的时候,类仅在有须要加载的时候被加载。假设你有一个应用须要的类叫做Abc.class,首先加载这个类的请求由 Application类加载器委托给它的父类加载器Extension类加载器,而后再委托给Bootstrap类加载器。Bootstrap类加载器 会先看看rt.jar中有没有这个类,由于并无这个类,因此这个请求由回到Extension类加载器,它会查看jre/lib/ext目录下有没有这 个类,若是这个类被Extension类加载器找到了,那么它将被加载,而Application类加载器不会加载这个类;而若是这个类没有被 Extension类加载器找到,那么再由Application类加载器从classpath中寻找。记住classpath定义的是类文件的加载目 录,而PATH是定义的是可执行程序如javac,java等的执行路径。
可见性的原理是子类的加载器能够看见全部的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
根据这个机制,父加载器加载过的类不能被子加载器加载第二次。虽然重写违反委托和单一性机制的类加载器是可能的,但这样作并不可取。
1)装载:查找并加载类的二进制数据;
2)连接:
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
3)初始化:为类的静态变量赋予正确的初始值;
类何时才被初始化:
1)建立类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类
1)若是这个类尚未被加载和连接,那先进行加载和连接
2)假如这个类存在直接父类,而且这个类尚未被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句
理论上Java由于有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被普遍使用于服务器端编程的一个重要缘由);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,所以也会致使内存泄露的发生。
GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java提供的GC功能能够自动监测对象是否超过做用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操做方法。Java程序员不用担忧内存管理,由于垃圾收集器会自动进行管理。要请求垃圾收集,能够调用下面的方法之一:System.gc()
或Runtime.getRuntime().gc()
,但JVM能够屏蔽掉显示的垃圾回收调用。
垃圾回收能够有效的防止内存泄露,有效的使用可使用的内存。垃圾回收器一般是做为一个单独的低优先级的线程运行,不可预知的状况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或全部对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,由于服务器端的编程须要有效的防止内存泄露问题,然而时过境迁,现在Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户一般以为iOS的系统比Android系统有更好的用户体验,其中一个深层次的缘由就在于Android系统中垃圾回收的不可预知性。
不能,虽然你能够调用System.gc()
或者Runtime.gc()
,可是没有办法保证 GC 的执行。
当经过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于建立堆空间,当程序中建立对象的时候,就从空间中分配内存。GC 是 JVM 内部的一个进程,回收无效对象的内存用于未来的分配。
JVM 中堆和栈属于不一样的内存区域,使用目的也不一样。栈经常使用于保存方法帧和局部变量,而对象老是在堆上分配。栈一般都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的全部线程共享。
能够经过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。经过这些方法你也能够获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory()
方法返回剩余空间的字节数,Runtime.totalMemory()
方法总内存的字节数,Runtime.maxMemory()
返回最大内存的字节数。
1)方法区(method):被全部的线程共享。方法区包含全部的类信息和静态变量。
2)堆(heap):被全部的线程共享,存放对象实例以及数组,Java堆是GC的主要区域。
3)栈(stack):每一个线程包含一个栈区,栈中保存一些局部变量等。
4)程序计数器:是当前线程执行的字节码的行指示器。
持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。全部新生成的对象首先都是放在年轻代的,年老代中存放的都是一些生命周期较长的对象。
内存溢出:程序申请内存时,没有足够的内存,out of memory;
内存泄漏值垃圾对象没法回收,可使用memory analyzer工具查看泄漏。