注:蓝色高亮代表还要查写的东西;红色字体用于阅读时提醒。
------------------------------How------------------------------------are----------------------------------------------------you?----------------------------
Q:请解释PATH和CLASSPATH的区别?
A:PATH是系统定义的环境属性,用于定义所有可执行程序的路径;
CLASSPATH是JVM定义的属性,在用java命令解释程序时会自动找到CLASSPATH的路径,进行类的加载。
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。
第一点. 接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。
第二点. 接口可以多继承,抽象类不行
第三点. 接口定义方法,不能实现,而抽象类可以实现部分方法。
第四点. 接口中基本数据类型为static 而抽类象不是的。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
对象内存分析
类本身属于引用数据类型,对于引用数据类型,必须对其进行内存分析。
堆内存:保存的是对象的具体属性信息;
栈内存:保存堆内存的地址(可理解为:对象名称)。
一块栈内存只能保存一块对应的堆内存地址。
第二种定义方法
Person per=null;//声明对象
per=new Person();//实例化对象
当没有实例化时,运行会发生 NullPointException;因为使用了没有开辟堆内存空间的引用对象。
(1)一块堆内存空间可以被多个栈内存所指向;
(2)如果一块堆内存空间没有任何栈内存指向,它会变成垃圾空间,所有垃圾空间会被GC回收并释放。垃圾回收时间时不确定的,要少产生。
构造方法:
方法名称与类名称相同;
如果一个类中没有构造方法,系统会自动提供一个无参构造。(所以,所有类中,肯定都有构造方法)。
作用:如果希望在对象实例化时传递一些参数,可用。
Q:既然构造方法没有返回值,那为什么不用void声明?
A:构造方法只在对象声明时调用一次;不同方法可以利用对象调用多次,如果是public void Person(){};这表示一个普通方法。
static:
主要用于描述全局概念;修改一个对象属性时,所有对象属性都会发生改变。对于这样的公共属性,不保存在栈,堆中,而保存在全局数据区中。
对于static定义的属性,可以用类名称直接调用。
所有非static属性,在对象实例化的时候才会进行内存的分配;
所有static属性,可以在没有实例化对象的时候进行调用。
使用情况:如果一个类中没有任何属性,那么这个类中不需要定义普通方法,只需要定义static方法。因为这样可以减少开辟无用的堆内存空间。
单例模式:
1.将该类的构造方法私有化,目的是禁止其他程序创建该类的对象;
2.在本类中自定义1个对象:
3.提供一个访问该类自定义对象的成员方法。
线程的几种可用状态:
1.新建;
2.可运行(runnable):线程对象创建后,其他线程(比如main)调用了该对象的start()方法,该线程位于可运行线程池中,等待被线程调度选中,获得CPU适用权;
3.运行(running):可运行的线程获得CPU的时间片(timeslide),执行程序代码;
4.阻塞(block):线程因为某原因放弃了CPU的适用权。阻塞分三种:
4.1等待~,运行的线程执行o.wait()方法,JVM会把该线程放入等待队列中;
4.2同步~,运行的线程在获取同步锁时,该同步锁被别的线程占用,JVM会把线程放到锁池中;
4.3其他~,运行的线程执行Thread.sleep(Long ms)或t.join();
5.死亡:线程run()或main()方法结束,或者由于异常退出了run()。死亡的线程不可再次复生。
死锁:
两个或以上的线程在执行过程中,因争夺资源而造成的一种相互等待现象。
产生原因:系统资源不足;进程运行顺序不合适;资源分配不当;
死锁的4个必要条件:
互斥,一个资源每次只能被一个进程使用;
请求与保持,1个进程因请求资源而阻塞,对以获得的资源保持不放;
不可剥夺:进程已获得的资源,在未使用完成前不能强行剥夺;
循环等待:若干线程形成一种头尾相接的循环等待资源关系;
-----------------------------------------------------------:)-------------------------------------------------
// 垃圾回收这里又忘了,把印象笔记里的重新抄一篇。完整版请参阅:http://blog.csdn.net/u012422829/article/details/46580893
java内存区域分析
1.运行时数据区域分析
运行时数据区域分为两部分,一部分时线程共享的(方法区,堆),另一部分时线程私有的(虚拟机栈,本地方法栈,程序计数器)
(1) 程序计数器:一块较小的内存空间,可看作是,当前线程所执行的字节码的行号指示器。
如果线程正在执行一个Java方法,计数器值为当前执行时虚拟机字节码指令地址;如果执行的时native方法,计数器值为空。
(注:被native修饰的方法叫本地方法,本地方法意味着和平台有关,移植性不高,native方法在JVM运行时数据区和其他方法不一样,它有专门的本地方法栈。native方法主要用于加载文件和动态链接库。是唯一不会出现OutOfMemoryError的区域)
(2)虚拟机栈:描述的是Java方法执行时的内存模型。
每个方法方法执行时都会创建一个栈帧,用于存储局部变量,操作数栈,动态链接,方法出口等信息。每个方法从调用到执行完的过程,都对应一个栈帧在虚拟机中入栈到出栈的过程。
(3)本地方法栈:作用与虚拟机栈类似。
区别:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,本地方法栈为虚拟机使用到的native方法服务,也会抛出StackOverFlowError和OutOfMemoryError异常
(4) java堆:被所有线程共享,在虚拟机启动时创建,它的目的是存放对象实例,几乎所有对象(和数组)都在这里分配内存。java堆是垃圾收集器管理的主要区域,也被称为GC堆。~处于物理上不连续的内存空间,只要逻辑上连续即可。
(5)方法区:所有线程共享,存储已被虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码数据。虽然有”永久代“之称,任然存在内存回收,主要针对常量池的回收和对类型的卸载,也会抛出OutOfMemoryError异常。
------------------------------------------------O_o------------------------------------------------
对象的创建
虚拟机遇到new指令时,先检查虚拟机是否加载了你要new的这个类(检查这个指令的参数能否在常量池中定位到一个类符号的引用),如果没加载,必须先执行相应的类的加载。
然后,为新生对象分配内存。
内存分配之后,虚拟机需要将内存空间都初始化为0。
接下来,对对象做必要的设置,例如,这个对象是哪个类的实例?如何才能找到类的元数据信息,对象的哈希码等,这些信息都存放在对象的头之中。
最后,执行程序员的意愿初始化。
类的加载机制
虚拟机把描述类的数据从class文件加载到内存中,并对数据进行校验,转换解析和初始化;最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
在Java中,类的加载,连接和初始化过程都是在程序运行期间完成的。
类从被加载到虚拟机内存中开始,到卸载出内存为止,整个生命周期包括:加载,【验证,准备,解析】(注:这三个动作称为:连接),初始化,使用和卸载。注:解析的顺序不是固定的,有时候能在初始化结束后才开始,以支持Java动态绑定。
下面写初始化时机
类加载过程:
1.加载
该阶段要完成以下三件事:
(1)通过一个类的全限定名来获取此类的二进制流
(2)将这个字节流所代表的静态存储结构转化为方法区运行时的数据结构
(3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
2.验证
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,因为Class文件的来源不一定时编译产生。甚至可以直接用十六进制编辑器直接编写。所以,如果不检查输入的字节流,对其完全信任的话,很有可能因为载入有害的字节流而导致系统崩溃
(大致可以分为4个阶段的检验 :1.文件格式验证 2.元数据验证 3.字节码验证 4.符号验证)
3.准备
是正式为类变量(被static修饰的变量)设置类变量的初始值(通常情况下数据类型值为0,具体赋值阶段时初始化阶段)。这些变量所使用的内存都将在方法区中进行分配,如果字段属性表中包含ConstantValue属性,那么准备阶段会设置为该值。
例如:public final static int a =123;
4.解析
是虚拟机将常量池中的符号引用替换为直接引用的过程
在Java中,一个Java类会编译成一个class文件,在编译时Java类并不知道引用类的实际地址,因此只能使用符号引用来代替。比如:org.simple.People类引用org.simple.Tool类,在编译时People类并不知道Tool类的实际内存地址,因此只能使用符号org.simple.Tool(假设)来表示Tool类的地址。而在类装载器装载People类时,此时可以通过虚拟机来获取Tool类的实际内存地址。因此便可以将符号org.simple.Tool替换为Tool类的实际地址,即直接引用。(注:直接引用与虚拟机实现的内存布局相关,引用目标必须加载到内存中;符号引用则不必须)。
5.初始化
在之前的阶段,除了加载阶段用户可以自定义类加载参与,其他阶段完全由虚拟机主导和控制,直到初始化阶段,才真正执行类定义的Java程序代码。
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何获取所需要的类。实现这个动作的代码模块称为类加载器。
--------------------------------------------------18.5.5--------------------------------------------------------
volatile关键字:
1)使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读到volatile变量,一定是最新的数据;
2)代码底层执行有对应的顺序,为了获得更好的性能,JVM会对指令进行重排,在多线程环境下可能会出问题。使用volatile关键字会禁止语义重排,当然这也一定程度上降低了代码的执行效率。
线程:一种CPU利用的基本单位,线程是CPU的基本单元;
进程:可看作是正在执行的程序。进程需要一定的资源(CPU时间,内存,文件和I/O设备)来完成任务。
多线程的优点:
1)响应度高2)资源共享3)经济4)多处理器体系结构的利用
sleep和wait的区别:
1)这两个方法来自不同的类,Thread,Object
2)sleep没有释放锁,而wait方法释放了锁(都可以用来放弃CPU的一定时间,不同在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会访问这个对象的监视器)
3)wait,notify,notifyAll只在同步控制方法或者同步控制块里面使用(这些方法在调用前都必须先获得对象锁),而sleep可以在任何地方使用。
一个线程如果出现了运行时异常会怎样?
如果这个异常没有被捕获,这个线程就停止执行了。
如果这个线程执有某个对象的监视器,那么这个对象监视器会被立即释放。
wait(),notify()/notifyAll()方法在放弃对象监视器时有什么区别?
wait()方法会立即释放对象监视器;
notify()/notifyAll()方法会等待线程剩余代码执行完毕再放弃对象监视器。
为什么要使用线程池?
避免频繁的创建和销毁线程,达到线程对象的重用;
线程池还可以根据项目灵活的控制并发的数目。
在JVM的规范中,有这么一些话:
“在JVM中,每个对象和类在逻辑上都是和一个监视器相关联的”
“为了实现监视器的排他性监视能力,JVM为每一个对象和类都关联一个锁”
“锁住了一个对象,就是获得对象相关联的监视器”
监视器好比一做建筑,它有一个很特别的房间,房间里有一些数据,而且在同一时间只能被一个线程占据,进入这个建筑叫做"进入监视器",进入建筑中的那个特别的房间叫做"获得监视器",占据房间叫做"持有监视器",离开房间叫做"释放监视器",离开建筑叫做"退出监视器".
而一个锁就像一种任何时候只允许一个线程拥有的特权.
一个线程可以允许多次对同一对象上锁.对于每一个对象来说,java虚拟机维护一个计数器,记录对象被加了多少次锁,没被锁的对象的计数器是0,线程每加锁一次,计数器就加1,每释放一次,计数器就减1.当计数器跳到0的时候,锁就被完全释放了.
java虚拟机中的一个线程在它到达监视区域开始处的时候请求一个锁.JAVA程序中每一个监视区域都和一个对象引用相关联.
怎么检测一个线程是否持有对象监视器?
Thread提供一个holdsLocal(Object obj)方法,当且仅当对象obj的监视器被某个线程持有时,才会返回true。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HTTP
它是基于TCP/IP的应用层协议,它被设计用于web浏览器和web服务器之间的通信,但它也可用于其他目的。
HTTP遵循客户端-服务器端模型,通过交换各自的信息来进行交互。通常由像浏览器这样的客服端发出的消息叫做request,被服务器回应的消息叫做response。
HTTP是无状态的:在同一个连接中两个成功的执行请求之间是没有关系的。例:这就带来了一个问题,用户没法在一个网站上进行连续签名。比如在一个电商网站,用户把某个商品加入到购物车中,换了一个页面后再次添加商品,两次添加请求没有联系,浏览器无法知道最终用户都选择了哪些商品。而用HTTP的头部拓展,HTTP cookie 就可以解决问题。把cookie添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,相同的状态。
HTTP能控制什么?
缓存:服务器能告诉代理和客户端什么需要被缓存,缓存多久,而客户端能根据请求条件首字段来忽略存储的文档。
开放同源限制:为了防止网络窃听和其他隐私泄露,浏览器强制对web网站做了分割限制。只有来源相同的网页才能获取网站的全部信息。
认证:一些页面能够保护起来,仅让特定的用户访问。
cookie:cookies用一个服务器的状态连接起了每一个请求,这就创建了会话。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
javac:负责编译部分,当执行javac时会启动java的编译器程序,对制定拓展名为.java文,件进行编译,生成jvm可以识别的字节码文件,即.class文件
java:负责运行部分,会启动jvm加载运行时所需要的类库,并对class文件进行执行
一个文件被执行时,必须要有一个执行的起点,这个起点就是main函数。
变量:内存中一个存储空间,用于存储常量数据。
变量开辟需要的要素:数据类型,变量名称,变量的初始化值。
基本数据类型:byte,short,int,long,float,double,char,boolean
引用数据类型:数组,接口,类
自动类型转换:从低级别到高级别,系统自动转换的。
强制类型转换:把一个高级别的数赋值给一个比该数级别低的变量。
对两个变量的数据进行呼唤,不需要第三方变量:
int a,b
a=a+b;
b=a-b;
a=a-b;
//
a=a^b;
b=a^b;
a=a^b;
高效算出2*8=2<<3
重载:在一个类中如果出现了两个或两个以上的同名函数,只要他们的参数个数,类型不同,即可称为重载。(和返回值没关系)
成员变量与局部变量的区别:
1.成员变量直接在类中定义,局部变量在方法中,参数中,语句中
2.成员变量在这个类中有效,局部变量只在自己所属的大括号内有效
3.成员变量存在于堆内存中,随着对象的产生而产生,消失而消失;局部变量存在于栈内存中,随着所属域的运行的存在而存在,结束而释放。
封装:指隐藏对象的属性和实现的细节,仅对外提供公共的访问方式。
this:代表对象,就是所在函数所属对象的引用。
this. 调用的是成员属性和成员方法;
this() 调用的是本类中对应参数的构造方法。
用this调用构造函数,必须定义在构造函数的第一行。因为构造函数是用于初始化的,所以初始化动作一定要执行,否则编译失败。
静态方法不能使用this,super关键字。因为this代表对象,而静态在时,有可能没有队形,所以无法使用。
成员变量与静态变量的区别:
1)成员变量所属对象,所以也称实例变量;
静态变量所属类,所以也称类变量
2)成员变量存在于堆内存中;静态变量存在于方法中;
3)成员变量随着对象的创建而存在,随着对象的被回收而消失;静态变量随着类的加载而存在,随着类的消失而消失。
4)成员变量只能被对象调用;静态变量可以被对象,类名调用。
继承:
好处:提高了代码的复用性,让类与类之间产生了关系,提供了另一个特征多态的前提,Java只支持单继承。
复写:
当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法,好像父类中的方法被覆盖掉了一样。
什么时候使用覆盖?当一个类的功能内容需要修改时,可以通过覆盖实现。
注意:子类覆盖父类时,必须保证,子类方法的权限必须大于父类方法的权限,否则,编译失败;
子类覆盖父类时,要么全是静态,要么全都不是。
子类构造函数运行时,先运行父类的构造函数,why?
子类的所有构造函数中的第一行,其实都有一条隐身的语句super()
子类继承父类时,会继承到父类中的数据,所以需要看父类是如何对自己初始化的。
final:
1)被final修饰的类是一个最终类,不能被继承;
2)被final修饰的方法不能被覆盖;
3)被final修饰的常量,只能赋值一次。
抽象类的特点:
抽象方法只能定义在抽象类中;
抽象方法只定义方法声明,并不定义方法实现;
抽象类不能被实例化
只有通过子类继承抽象类并覆盖了抽象类中的所有方法后,该子类才能被实例化