本文主要是介绍需要时常回顾的java基础知识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Java装载和编译原则
1. Java的装载过程为:先装载类,然后初始化静态变量
2. Java编译器要保证所有静态成员在使用前都分配好自己的内存空间,而非静态成员只有在使用的时候才能分配内存。(静态内部类中定义了静态成员,那么该类就一定是静态的)
3. 如果内部类不是静态的,则按照1,2条,非静态内部类只有在外部类被实现之后才能初始化
4. 按照3,只有非静态内部类初始化之后,内部类中的静态变量才能被初始化。
5. 这样的话4与2相悖,因为java编译器要保证所有静态变量在使用前都被初始化,并分配内存
6. 这样,要保证java编译器原则,如果要在内部类中定义静态成员,内部类也应该定义成静态,保证外部类一加载所有静态成员全部初始化,并拥有自己的内存空间,以备使用
JVM的流程:
加载 -- 连接 -- 初始化 -- 使用 -- 卸载
在卸载时:
在类使用完之后可能会被卸载,可能性如下:
如果有下面的情况,类就会被卸载:
1. 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
2. 加载该类的ClassLoader已经被回收。
3. 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了。
java中几个常见的存储区域:
1. 方法区:在java的虚拟机中有一块专门用来存放已经加载的类信息、常量、静态变量以及方法代码的内存区域,叫做方法区。
2. 常量池:常量池是方法区的一部分,主要用来存放常量和类中的符号引用等信息。
3. 堆区:用于存放类的对象实例。
4. 栈区:也叫java虚拟机栈,是由一个一个的栈帧组成的后进先出的栈式结构,栈桢中存放方法运行时产生的局部变量、方法出口等信息。当调用一个方法时,虚拟机栈中就会创建一个栈帧存放这些数据,当方法调用完成时,栈帧消失,如果方法中调用了其他方法,则继续在栈顶创建新的栈桢。
创建person.class对象的整个流程
Person p;
加载类阶段
1. 加载class文件:因为new用到person.class,所以会先找到person.class文件并加载到内存中(方法区);
2. 执行静态代码块:执行该类中的static代码块,给person.class类进行初始化;
3. 开辟类内存:在堆中开辟空间,分配内存地址(堆区)。
4. 初始化静态属性:开辟空间存储静态变量并对静态数据进行初始化(方法区)
5. 存储静态函数:在内存中为静态函数开辟空间(方法区)。
存储对象阶段
6. 为属性开辟空间:在存中建立对象的特有属性,并对其进行默认初始化初始化(堆区)
7. 初始化属性:对属性进行显示地初始化(堆区)
8. 执行构造代码块:对对象进行构造代码块初始化:执行构造代码块(堆区)
9. 执行构造函数:对对象进行构造函数初始化。(堆区)
10. 将地址赋给对象:将内存地址赋给栈内存中的变量p
单例设计模式
单例模式:希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
应用:一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
注意点:如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。
1. 饿汉模式
Single类一进内存就已经初始化了对象
1) 为了保证让类自己负责保存自己的实例,类中的变量都为private,并且构造函数也为private,不让外界创建;
2) 为了保证对象随时都可以被访问,以及其唯一性,因此将对象定义为static 并在内部自己创建
3) 为了能使外部得到该对象,我们暴露了获得对象的方法。
2. 懒汉模式
Single类进入内存,对象还未存在,只有当调用方法时才建立对象
模板类方法设计模式:
定义功能时,功能的一部分是确定的,而确定的部分在使用不确定的部分时,那么就需要将不确定的部分暴露出去,由子类去完成。即:去外面建立一个类,等子类去复写。
优点:降低了具体链接和程序的耦合性,提高了功能扩展性,提供了规则。
里面的final防止该函数被复写。
对象可访问状态:
状态 | Public | Protected | Default | Private |
同一包中 | OK | OK | OK | NO |
同一类中 | OK | OK | OK | OK |
子类间 | OK | OK | OK | NO |
不同包中 | OK | OK | NO | NO |
线程执行状态图:
死锁:
Eg:一人一支筷子,相互请求吃饭。
原因:同步中嵌套同步,锁却不同。
程序例子
这里的锁为了好区分,定义了MyLock对象。在完美情况下,t1拿到执行权在21行等待t2。这时t2获取执行权执行接下来的代码。如果运气差的话,t1拿到执行权,去等待锁b,而这时t2也刚刚建立,执行到32行等待锁a,于是两个就互相等,形成死锁。归结到底是执行权的问题;
多线程间通信
在对线程通信过程中,每个对象都要用到同一个资源库,而Synchrinizied则是为资源库上锁,防止一个资源在同一时间被多个用户使用。资源库需要自带标志,以表明自己此时是否被使用。而在唤醒线程之前需要循环查看此标志。
集合继承关系
集合容器并发访问错误:
不能对同一元素进行不同方式的操作:
Eg:Iterator it = a1.iterator(); // 并发访问错误。
a1.add(“java001”);
分析:既用了迭代器有用到了原对象,从而引发并发修改异常。
并发访问错误的原因:
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
在使用迭代器时,不能通过集合对象方法操作集合中的元素。可是iterator方法有限,只能对元素进行判断,取出,删除操作。若要想进行其他操作eg添加、修改。就需要使用其子接口:ListIterator;
这篇关于需要时常回顾的java基础知识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!