1.java中==和equals和hashCode的区别
1.==
1)如果是基础数据类型(byte,short,char,int,long,float,double,boolean)比较是值;
2)如果是类,接口,数组比较的是地址
2.equals
1)默认情况下(没有覆盖)equals方法都是调用Object类的equals方法,用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)
3.HashCode
Java中的集合。
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
覆盖equals时总要覆盖hashCode
a == b -> hashCode(a) == hashCod(b)
a != b hashcode(a)与hashcode(b)可以相等
2.Java中HashMap、Hashtable、ConcurrentHashMap的原理与区别
HashTable
1)底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
2)初始size为11,扩容:newsize = olesize*2+1
3)计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
1)底层数组+链表实现,可以存储null键和null值,线程不安全
初始size为16,扩容:newsize = olesize*2 一定是2的倍数
计算index的方法:index = (hash & (tab.length - 1))
2)哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较
3)加载因子:HashMap和Hashtable的构造器允许指定一个负载极限为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
注:HashMap和Hashtable的构造器允许指定一个负载极限,
“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:
ConcurrentHashMap
1)底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry
的value变量是 volatile的,也能保证读取到最新的值。)
2) Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
3)有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
4(扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null
3.volatile
1.不能保证原子性
2.能保证可见性,直接修改内存而不是缓存值
3.通过禁止指令重排序一定程度的有序性
4.Java多态
多态三个前提
> 1.要有继承关系
> 2.子类要重写父类的方法
> 3.父类引用指向子类对
5.String、StringBuffer与StringBuilder之间区别
1.运行速度:StringBuilder > StringBuffer > String 应为String是字符串常量而StringBuilder和StringBuffer是字符串变量
2.StringBuilder线程不安全,StringBuffer线程安全
6.内部类
> 成员内部类
1.成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
2.当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
3.在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
> 局部内部类
1.定义在一个方法或者一个作用域里面的类
2.局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
> 匿名内部类
1.好处是使代码更加简洁,紧凑,但是带来的问题是易读性下降。
2.比如android中onClick()
> 静态内部类
1.和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法
2.静态内部类是指被声明为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型)
7.接口和抽象类的区别
1.概念不同:接口是指动作的抽象,抽象类指的是根源的抽象,所以只能继承一个抽象类但可以实现多个接口
2.实例化时对抽象类是去继承,而接口是去实现。如果要实例化,抽象类需要指向子项
3.接口里变量只能用final static修饰
4.抽象类 抽象类中可以没有抽象方法,但有抽象方法的一定是抽象类。
8.泛型中extends和super的区别
泛型:创建集合时就指定元素类型,该集合只能保存其指定类型的元素
泛型擦除:将泛型java代码直接转换成普通java字节码。
1.<? extends T>限定参数类型的上界:参数类型必须是T或T的子类型
2.<? super T> 限定参数类型的下界:参数类型必须是T或T的超类型
9.父类的静态方法能否被子类重写
答:不能重写,也就是不能被覆盖!但是,与实例方法一样,子类能把父类的静态方法继承过来。但是子类可以重新定义父类的静态方法,并将父类的静态方法屏蔽掉。取消屏蔽可以使用 *父类名.静态方法名*的方式调用
10.进程和线程的区别
1.进程是资源分配的最小单位,线程是程序执行的最小单位
2.进程有独立的地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵,而线程共享这些。但多进程更加健壮,多线程只要有一个线程死了整个进程就死了
11.final,finally,finalize的区别
1.final
1).修饰类
当final修饰类时表明该类不能被其他类所继承,而且final类中所有的成员方法都会隐式的定义为final方法
2).修饰方法
a.把方法锁定,以防止继承类对其进行更改。
3). 修饰变量
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于C++中的const。
2.finally
3.finalize()
这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
12.GC
GC的工作流程主要分为如下几个步骤:
1、标记(Mark)
2、计划(Plan)
3、清理(Sweep)
4、引用更新(Relocate)
5、压缩(Compact)
1.垃圾回收的意义
a.当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。
b.可以清除内存记录碎片
c.优点:它能使编程效率提高,保护程序的完整性
d.缺点:销影响程序性能。Java虚拟机必须追踪运行程序中有用的对象,而且最终释放没用的对象;
销影响程序性能。垃圾回收算法的不完备性
2.垃圾收集的算法分析
a.垃圾回收首先需要确定从根开始哪些是可达的和哪些是不可达的,从根集可达的对象都是活动对象,它们不能作为垃圾被回收,这也包括从根集间接可达的对象。
2.1. 引用计数法(Reference Counting Collector)
引用计数法是唯一没有使用根集的垃圾回收的方法。堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。运行较快但增加了开销,并且无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0.
2.2. tracing算法(Tracing Collector)
使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象,会造成内存碎片
2.3 compacting算法(Compacting Collector)
在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。
2.4 copying算法(Compacting Collector)
2.5 generation算法(Generational Collector)
copy垃圾收集器的一个缺陷是收集器必须复制所有的活动对象,而Generation法将堆分成两个或多个 ,每个子堆作为对象的一代 (generation)垃圾收集器将从最年轻的子堆中收集这些对象
13.序列化
1).什么是序列化
序列化:将对象转换为字节流
反序列化:将字节流转换成对象
2).为什么序列化
a.永久性保存对象,保存对象的字节序列到本地文件中;
b.通过序列化对象在网络中传递对象;
c.通过序列化在进程间传递对象
3).序列化的方式
a.Java原生序列化
Java原生流(InputStream和OutputStream之间的转化)的方式进行转化。需要注意的是JavaBean实体类必须实现Serializable接口
b.Json序列化
c.FastJson
是由阿里巴巴开发的一个性能很好的Java 语言实现的 Json解析器和生成器。特点:速度快,测试表明fastjson具有极快的性能,超越任其他的java json parser。使用时候需引入FastJson第三方jar包。FastJson序列化代码示例如下所示:
14.静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
父类的静态属性和方法可以被子类继承,不可以被子类重写
原因:
1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。
3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态
15.闭包
闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的自由变量。
如何用变量去存储方法?
java中能够保存方法的变量指的就是普通的对象
如何让这个普通对象能够访问所在类的自由变量?
纯天然的解决办法是:内部类。内部类能够访问外部类的所有属性及方法。
隐藏具体实现是内部类的作用之一,如何保证隐藏具体实现的同时还能将闭包传递到外部使用?
让内部类实现通用接口,然后将内部类对象向上转型为接口类型。
上述解决办法就是Java最常用的闭包实现办法(内部类+接口)
16.编码方式
1.ASCII->GBK->UTF-8->Unicode
UTF-8
a.如果第一位为0代表ASCII码
b.如果前两位为11,有多少个连续的1就有多少个字节
c.如果前两位为10说明不是第一个字节需要向前查找
17.fail-fast机制
机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
18.happen-before
1.程序顺序:一个线程的每个操作先于每个happen-before该线程的任何后续操作
2.监视器锁规则:对一个监视器的解锁happen-before于随后对这个监视器的加锁
3.volatile变量规则:对一个volatile的写,happen-before于任意后续对这个volatile的读
4.传递性 if a happen-before b and b happen-before c ->a happen-before c;
5.线程启动规则:Thread对象的start()方法happen-before于此线程任何动作
19.mvc
在WEB中表现层的总称
20.构造器Constructor是否可被override
构造器不能被重写,不能用static修饰构造器,只能用public,private,protected三种权限修饰符,不能有返回值
21.vector ArrayList LinkedList
1.ArrayList和Vector的区别
都实现了List接口,都是通过数组实现的,vector线程安全而ArrayList线程不安全;
List第一次创建的时候,会有一个初始大小,随着不断向List中增加元素,当List 认为容量不够的时候就会进行扩容。Vector缺省情况下自动增长原来一倍的数组长度,ArrayList增长原来的50%。
2.ArrayList和LinkedList
ArrayList的底层是数组实现的,可以认为ArrayList是一个可改变大小的数组,而LinkedList底层是双向链表,增删速更快但查询和修改值更慢。两者的区别主要是数组和链表的区别。
22.自动装箱和拆箱
1.装箱:将基本类型用他们对应的对象包装起来
2.拆箱:将包装对象转换成基本数据类型
23.Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
24.Java线程池
1.什么是线程池
java.util.concurrent.Executors提供一个接口的实现用于创建线程池
主要解决处理器多个线程执行的问题,可以显著减少处理器单元的闲置时间增加处理器的吞吐时间。
如果创建线程时间(T1)+在线程中任务执行的时间(T2)远大于销毁的时间(T3),则可以采用线程池
2.线程池的主要组成部分
1).线程池管理器
用于创建和管理线程池,包括创建线程池、销毁线程池、添加新任务
2).工作线程
位于线程池中的线程,在没有任务时处于等待状态,可以循环的执行任务
3).任务接口
每个任务必须实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口,执行完任务的收尾任务,任务的执行状态等
4).任务队列
用于存放没有处理的任务,提供一种缓存机制
线程池关注如何缩短或调整T1和T3的技术,把T1和T3安排在服务器程序的启动和结束空闲的时间段
25.栈和堆
1.内存占用分为
1).栈区 2).堆区 3).全局静态区
4).文字常量区 5).程序代码区
2.堆和栈的区别
1).栈是由系统自动分配,堆是由程序员自己申请
2).栈只要申请空间大于剩余空间,系统就为程序提供内存,而堆需要遍历链表查找第一个大于申请空间,容易产生碎片
3).栈由系统分配速度较快,但程序员无法控制,堆速度较慢而且容易产生碎片,但是用起来方便
26.多线程线程停止的正确方式
1).stop()方法,不推荐。
该方式是通过立刻抛出ThreadDeath异常,而且抛出该异常有可能发生在程序的任何一个地方,包括catch,finally等语句块中,同时会导致该线程释放掉所有的锁可能会导致线程安全问题以及数据不一致问题。
2).使用volatile标志位
3).使用中断机制。用interrupt()方法中断正在运行中的线程只会修改中断状态位,可以通过isInterrupted()判断。如果使用interrupt()方法中断阻塞中的线程,那么就会抛出InterruptedException异常
27.多个线程之间共享数据
1.多个线程对共享数据的操作是相同的,那么创建一个Runnable的子类对象,将这个对象作为参数传递给Thread的构造方法
2.多个线程对共享数据的操作是不同的,将共享数据和操作共享数据的方法放在同一对象中,将这个对象作为参数传递给Runnable的子类,在子类中用该对象的方法对共享数据进行操作。如:生产者消费则。
3.多个线程对共享数据的操作是不同的, 用内部类的方式去实现,创建Runnable的子类作为内部类,将共享对象作为全局变量,在Runnable的子类中对共享数据进行操作。
28.wait,notify,notifyall
1.锁池和等待池
2.notify和notifyall区别
调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒他等待该对象锁的线程依然会处于wait()中,而调用notifyAll()后所有等待该对象锁的线程都会结束wait()转而去竞争对象锁,而没有抢到对象锁的线程会回到等待状态(这里的等待状态是线程的一个状态).
如果所有线程都在等待相同的条件,并且一次只有一个线程可以从条件变为true中受益,则可以使用notify over notifyAll。
29.单例模式
1.单例模式解决一个类在内存中只有一个对象
2.解决思路:
1).将构造函数私有化
2).在类中创建一个本类对象
3).提供一个方法可以获取到对象
3.两种方法
1).饿汉式:类创建时分配对象
class Single
{
private Single(){}
//类建立时即初始化了对象
private static Single s = new Single();
public static Single getInstance()
{
return s;
}
}
2).懒汉式:调用时分配对象
class Single
{
private Single(){}
private static Single s;
public static Single getInstance()
{
//调用时再分配对象
if(s == null)
s = new Single();
return s;
}
}//但是此懒汉式如果是多线程调用,容易出现new多个对象的问题
30.反射
1.获取Class的三种方式
1).Object -> getClass()
2).任何数据类型包括基本数据类型都有一个静态的class类型
3).通过Class类的静态方法:forName(String className)
2.通过反射获取构造方法并使用
3.获取成员变量并调用
stuClass.getFields();//获得所有公有的字段
stuClass.getDeclaredFields();//获得所有的字段
stuClass.getField("name")
4.获取成员方法并调用
5.反射main方法
6.通过反射运行配置文件内容
7.通过反射越过泛型检查
31.java创建对象的几种方法
1.new
2.反射
3.clone()
4.序列化
32.String类intern()方法
intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回。
33.引用的4种类型
在jdk 1.2以前,创建的对象只有处在可触及(reachaable)状态下,才能被程序所以使用,垃圾回收器一旦发现无用对象,便会对其进行回收。但是,在某些情况下,我们希望有些对象不需要立刻回收或者说从全局的角度来说并没有立刻回收的必要性。比如缓存系统的设计,在内存不吃紧或者说为了提高运行效率的情况下,一些暂时不用的对象仍然可放置在内存中,而不是立刻进行回收。
1.强引用
如果一个对象具有强引用,则无论在什么情况下,GC都不会回收被引用的对象。当内存空间不足时,JAVA虚拟机宁可抛出OutOfMemoryError终止应用程序也不会回收具有强引用的对象
2.软引用(Soft Reference)
表示一个对象处在有用但非必须的状态。如果一个对象具有软引用,在内存空间充足时,GC就不会回收该对象;当内存空间不足时,GC会回收该对象的内存(回收发生在OutOfMemoryError之前).
3.弱引用(Weak Reference)
具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。
4.虚引用
就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。虚引用和弱引用的区别在于:虚引用在使用时必须和引用队列(ReferenceQueue)联合使用。GC在回收一个对象时,如果发现该对象具有虚引用,那么在回收之前会首先该对象的虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入虚引用来了解被引用的对象是否被GC回收。
34.守护线程和非守护线程
程序运行结束,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程,最典型的守护线程就是GC线程
35.怎么检测一个线程是否持有对象监视器
Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法
36.Runnable和Callable的区别
1.Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
2.Future,FutureTask
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
FutureTask是Future接口的一个唯一实现类
37.wait(),notify()和suspend(),resume()之间的区别
wait()、notify() :当线程调用wait()方法后会进入阻塞状态,进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。 需要注意的是执行notify后,并未立即释放锁,而是在执行完 notify所在 synchronized 的代码块后 才释放锁的, 因此被此锁阻塞的的线程需要在 notify所在 synchronized 的代码块后 才能进行可执行状态。
suspend(),resume()这对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。
38.为什么wait, nofity和nofityAll这些方法不放在Thread类当中
1.一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
39.ThreadLocal
1.什么是线程局部变量ThreadLocal
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享,是一种实现线程安全的方式。
但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
2.ThreadLoacl
简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。
40.Java中线程调度算法
1.Java用到的线程调度算法
抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿程度等算出一个总的优先级并分配下一个时间片给某个线程执行
2.Thread.slepp(0)的作用
由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作
41.CAS(Compare and Swap)
1.锁机制的问题
1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和diao调度延迟,会引发性能问题
2)一个线程持有锁会导致其他需要锁的进程挂起
3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
2.什么是CAS
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
42.类变量和成员变量区别
1.所属不同:静态变量属于类所以又称为静态变量而成员变量属于对象
2.内存中位置不同:静态变量属于静态区成员变量存储于堆内存
3.内存出现时间不同:静态变量随着类的加载而加载随着类的消失而消失,成员变量随着对象的加载而加载随着对象的消失而消失
4.静态变量可以通过类名也可以通过对象名调用
5.静态方法只可以访问静态方法和静态变量。