Java基础中的那些坑_第1页
Java基础中的那些坑_第2页
Java基础中的那些坑_第3页
Java基础中的那些坑_第4页
Java基础中的那些坑_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、一、基本类型的问题类型 默认值 取值范围 举例byte : 0 -2727-1 byte b=10;char : u0000 u0000-uFFFF char c=c ;short : 0 -215215-1 short s=10;int : 0 -231231-1 int i=10;long : 0 -263263-1 long o=10L;float : 0.0f -231231-1 float f=10.0Fdouble : 0.0d -263263-1 double d=10.0;boolean: false truefalse boolean flag=true;byte+byte=

2、int,低级向高级是隐式类型转换,高级向低级必须强制类型转换,bytecharshortintlongfloat=”或“=-err) & (x=err)或者用|x-0|err来判断,这里|x-0|表示绝对值,err表示限定误差。 用程序表示就是 fabs(x)0.00001f二、 运算符的问题floor 返回不大于的最大整数,返回double类型 math.floor(-0.5)的结果为-1.0ceil 返回不小于的最小整数,返回double类型 math.ceil(-0.5)的结果为-0.0round 则是对小数4舍5入的计算,返回int类型 round方法,它表示“四舍五入”,算法为Mat

3、h.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。 混合赋值运算符的使用表示带符号右移位表示无符号右移但是没有为带符号右移,右移后左边的空位被填充为符号位为不带符号右移,右移后左边的空位被填充为0没有 ,因为后右边总是补0短路逻辑运算符和非短路逻辑运算符。&和|为非短路逻辑运算符,不按“短路”方式计算。即在得到计算结果之前,一定要计算两个操作数的值。无论两个语句为真还是为假,两个都得执行,一定会执行完毕。&和|为短路逻辑运算符,是按照“短路”方式求值的。如果第一个操作数已经能

4、够确定表达式的值,第二个操作数就不必计算了。计算余弦值使用Math类的cos()方法。toRadians()是将角度转换为弧度;toDegrees()是将弧度转换为角度。三、多线程问题Java中的多线程是一种抢占式的机制,而不是分时机制。抢占式的机制是有多个线程处于可运行状态,但是只有一个线程在运行。 共同点 : 1. 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。 2. wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。 如果线程A希望立即结束线程B,则可以对线程B对应的T

5、hread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() 中直接return即可安全地结束线程。 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。 不同点

6、 : 1.每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。 sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。 2.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 3.sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。4.wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池

7、,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池,准备获得对象锁进入运行状态(这时还是准备状态,等待CPU资源进入运行状态)。首先:创建并启动线程的过程为:定义线程实例化线程启动线程。一 、定义线程: 1、继承java.lang.Thread类。 2、实现java.lang.Runnable接口。二、实例化线程: 1、如果是扩展java.lang.Thread类的线程,则直接new即可。 2、如果是实现了java.lang.Runnable接口的类,则用Thread的构造方法: Thread(Runnable target) Thread(Runnable

8、target, String name) Thread(ThreadGroup group, Runnable target) Thread(ThreadGroup group, Runnable target, String name) Thread(ThreadGroup group, Runnable target, String name, long stackSize)三、启动线程: 在线程的Thread对象上调用start()方法,而不是run()或者别的方法。run和start的区别一个是调用方法,一个是启动线程。所以当仅仅调用run()方法时,并没有创建一个新的线程, 就是普通

9、的对象的调用方法,并不会并发执行。开启线程是调用start()方法,这样才能并发执行。同步方法和同步代码块的区别是什么?同步方法 ( 粗粒度锁 ): 1. 修饰一般方法 : public synchronized void method ()., 获取的是当前调用对象 this 上的锁 2. 修饰静态方法 : public static synchronized void method ()., 获取当前类的字节码对象上的锁同步代码块 ( 细粒度锁 ): synchronized ( obj ) ., 同步代码块可以指定获取哪个对象上的锁 , obj 任意resume与suspended一起使

10、用:suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的 resume() 被调用,才能使得线程重新进入可执行状态 wait与notify(notifyAll)一起使用,让线程暂时不执行 sleep会让线程暂时不执行sleep和wait的区别有: 1,这两个方法来自不同的类分别是Thread和Object 2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得敏感词线程可以使用同步控制块或者方法。 3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 synchronized(x) x.notify

11、() /或者wait() 4,sleep不出让系统资源,只是让当前线程暂停执行;wait是进入线程等待池等待,出让系统资源,敏感词线程可以占用CPU。Thread.sleep() 和 Object.wait(),都可以抛出 InterruptedException。这个异常是不能忽略的,因为它是一个检查异常(checked exception),需要本函数必须处理的,用try和catch处理,或者用throws抛出异常,然后交给调用者去处理异常。当前线程wait()执行后释放锁,进入线程等待,直到其他线程使用notify()方法将其唤醒。notify()就是对对象锁的唤醒操作。但有一点需要注意

12、的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized()语句块执行结束才释放锁。自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。CopyOnWriteArrayList适用于写少读多的并发场景ReadWriteLock即为读写锁,他要求写与写之间互斥,读与写之间互斥, 读与读之间可以并发执行。在读多写少的情况下可以提高效率ConcurrentHashMap是同步的HashMap,读写都加锁实现线程间通知和唤醒:Object.wait/notify/notifyAll以

13、及Condition.await/signal/signalAll线程间同步和互斥是调用Lock对象的Condition对象的。它提供了三个方法: await/signal/signalAll方法。await方法可以让当前线程处于等待状态,直到某条件发生signal方法用于唤醒一个等待的线程,signalAll方法用于唤醒所有等待的线程。Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协

14、作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。Condition是个接口,基本的方法就是await()和signal()方法;Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()。调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用Conditon中的await()对应Object的wait();Condition中的signal()对应Object的n

15、otify();Condition中的signalAll()对应Object的notifyAll()wait/notify/notifyAll是Object类的是本地方法,并且为final方法,无法被重写同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。JDK提供的最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier 和Exchanger。Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。CyclicB

16、arrier 主要的方法就是一个:await()。await() 方法每被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。 直译过来就是倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。线程结束的三个原因:1、run方法执行完成,线程正常结束2、线程抛出一个未捕获的Exception或者Error3、直接调用该线程的Stop方法结束线程(不建议使用,容易导致死锁)四、多态问

17、题A派生出子类B,B派生出子类C。1.A a0=new A();2.A a1=new B();3.A a2=new C();继承具有多态性,可以是本类的引用指向本类对象,也可以是本类的引用指向子类对象多态的特性,类型向上转型。假设子类B中除了重写了父类的方法外,还有自己的方法,那么a1想调用子类自身的方法时,必须先通过B b = (B)a1;强制转换后,再调用,不然编译不通过。如果a1想调用子类和父类中都有的方法,用a1直接就可以调用,不需要强制转换,但调用的其实是子类中重写后的方法。五、集合问题java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口

18、方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。collection类型的集合只能装入对象类型的数据,但是JDK5以后提供了自动装箱与自动拆箱,所以如果装入的是一个基本类型,例如int类型自动装箱变为了Integer类型。编译能够正常通过。set 元素无序,不可重复list 元素有序,可重复Set中关于元素重复的定义,如果添加的元素

19、未定义自己的equels方法的话,调用默认的,也就是直接=。如果添加的是字符串,而字符串已经实现了equels方法,所以不会产生上面中提到的问题,但是自己定义的类就要注意了。HashSet不能添加重复的元素,当调用add(Object)方法时候, 首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素; 如果已存在则调用Object对象的equals方法判断是否返回true, 如果为true则说明元素已经存在,如为false则插入元素。ArrayList的构造函数总共有三个:(1)ArrayList()构造一个初始容量为 10的空列表。(2)Arra

20、yList(Collection c)构造一个包含指定collection的元素的列表,这些元素是按照该collection的迭代器返回它们的顺序排列的。(3)ArrayList(int initialCapacity)构造一个具有指定初始容量的空列表。ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。这里的所谓动态数组并不是那个“ 有多少元素就申请多少空间 ”的意思,通过查看源码,可以发现,这个动态数组是这样实现的,如果没指定数组大小,则申请默认大小为10的数组,当元素个数增加,数组无法存储时,系统会另个申请一个长度为当前长度1.5倍的数组,然后,把之

21、前的数据拷贝到新建的数组。对于随机访问get和set,ArrayList觉得优于LinkedList。因为ArrayList是数组,可以直接定位到相应位置取元素,而LinkedLIst是链表,需要移动指针从前往后遍历。对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据,ArrayList的新增和删除就是数组的新增和删除,LinkedList与链表一致。ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。因为ArrayList空间的增长率为1.

22、5倍,所以,最后很可能在结尾留下一部分空间是没有用到的,因此,会造成浪费的情况。对于LInkedList的话,由于每个节点都需要额外的指针,所以,每一个元素都需要消耗相当的空间。HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对)。插入的时候,检查是否已经存在相同的key,如果不存在,则直接插入,如果存在,则用新的value替换旧的value。HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。Hashtable的方法是Synchronize的,而HashMap不是,在多个线

23、程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。由所有HashMap类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。Hashtable和HashMap的区别主要是前者是同步的,后者是快速失败机制保证。在判断一个对象是否存在于HashMap或者Hashtable中时,Hashtable的方法是contains(Object value)

24、、containsKey(Object key)、containsValue(Object value) HashMap的方法是containsKey(Object key)、containsValue(Object value) 注意,没有contains方法。1. HashMap,TreeMap 未进行同步考虑,是线程不安全的。2. HashTable 和 ConcurrentHashMap 都是线程安全的。区别在于他们对加锁的范围不同,HashTable 对整张Hash表进行加锁,而ConcurrentHashMap将Hash表分为16桶(segment),每次只对需要的桶进行加锁,使用

25、ReentrantLock来保证线程安全。3. Collections 类提供了synchronizedXxx()方法,可以将指定的集合包装成线程同步的集合。比如,List list = Collections.synchronizedList(new ArrayList();Set set = Collections.synchronizedSet(new HashSet();Map map = Collections.synchronizedMap(new HashMap();Hashmap使用的是链地址法解决哈希冲突。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体

26、。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。源码程序中用到了一个重要的内部接口:Map.Entry,每个 Map.Entry 其实就是一个 key-value 对。当系统决定存储 HashMap 中的 key-value 对时,完全没有考虑 Entry 中的 value,仅仅只是根据 key 来计算并决定每个 Entry 的存储位置。Entry是数组,数组中的每个元素上挂这个一条链表。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。很显然我们使用的不是开放地址法

27、。如果在循环的过程中调用集合的remove()方法删除集合的某个元素时,就会导致循环出错,例如: for(int i=0;ilist.size();i+) list.remove(.); 循环过程中list.size()的大小变化了,就导致了错误。 所以,如果你想在循环语句中删除集合中的某个元素,就要用迭代器iterator的remove()方法,因为它的remove()方法不仅会删除元素,还会维护一个标志,用来记录目前是不是可删除状态,例如,你不能连续两次调用它的remove()方法,调用之前至少有一次next()方法的调用。六、String类型String对象的不可变性。Java中的字符串

28、存储在字符串常量区,不会改变,即使发生改变其实也是新创建一个对象。对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象。toUpperCase()会对当前对象进行检查,如果不需要转换直接返回当前对象,否则new一个新对象返回; String x=fmn;x.toUpperCase();System.out.println(x); /fmnx = x.toUpperCase();System.out.println(x); /FMNreplace()如果两个参数相同,则直接返回,否则new一个新对象返回。String 字符串常量 StringBuffer 字符

29、串变量(线程安全) StringBuilder 字符串变量(非线程安全)String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都是String类的对象。字符串是常量;它们的值在创建之后不能改变。所以可以共享它们。StringBuffer是字符串缓存区,它的内容可以被修改,长度也可以改变,StringBuffer类是线程安全的,也就是说多个线程可以安全的访问StringBuffer对象。StringBuilder与StringBuffer类似,都是字符串缓冲区,StringBuilder和StringBuffer底层都是以字符数组存放的,可以修改内容。但StringB

30、uilder不是线程安全的,如果你只是在单线程中使用字符串缓冲区,那么StringBuilder的效率会更高些。从执行效率上来看StringStringBufferStringBuilder。线程安全:(Buffer是一个资源,有锁访问的,线程安全的)非线程安全的情况:当A获取到这条数据,准备修改时,B线程进来了,校验完数据,发现数据不正确,就把数据移除了,这时A线程仍旧认为当前持有的数据还是开始获取的数据,这样再做处理就会报空了。线程安全的情况:就是当一个线程需要对这个数据进行改写时,会给这个数据上一个同步锁,比如A线程拿到数据后,给数据加上同步锁,这时候B线程进来了,但是这时候数据已经上锁

31、,B线程则需要等待A线程释放锁之后才能对数据进行操作,这样就防止了脏数据的产生。值得注意的是StringBuilder是在JDK1.5版本中增加的。以前版本的JDK不能使用该类。String对String 类型进行改变的时候其实都等同于生成了一个新的String对象,然后将指针指向新的String对象;StringBuffer 每次结果都会对StringBuffer对象本身进行操作,而不是生成新的对象,再改变对象引用。 七、异常问题都是Throwable的子类: 1.Exception(异常):是程序本身可以处理的异常。 2.Error(错误): 是程序无法处理的错误。这些错误表示故障发生于虚

32、拟机自身、或者发生在虚拟机试图执行应用时,一般不需要程序处理。比如OutOfMemoryError。3.检查异常(编译器要求必须处置的异常):除了Error,RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。典型的检查异常包括IOException、SQLException等。4.非检查异常(编译器不要求处置的异常): 包括运行时异常(RuntimeException与其子类)和错误(E

33、rror)。典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。Java语言中的异常处理包括声明异常、抛出异常、捕获异常和处理异常四个环节。throw用于抛出异常。throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异常对象。try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。cacth用于捕获从try中抛出的异常并作出处理。finally语句块是不管有没有出现异常都要执行的内容。try-

34、catch-finally获取异常错误的结构。try中有两种情况: 1.try中没有异常时,但是有return等跳转语句,这样会引发程序控制流离开当前的try,自动完成finally中资源的释放。(假设利用 return 语句从 try 语句块中退出。在方法返回前,finally子句的内容将被执行。如果 finally 子句中也有一个 return 语句,这个返回值将会覆盖原始的返回值。)2.try中有异常时,catch在获取到异常之前,进行finally执行,接着执行catch中的语句。如果捕获到了一种类型的异常,后面层级更高的异常就不会执行了。checked exception:指的是编译

35、时异常,该类异常需要本函数必须处理的,用try和catch处理,或者用throws抛出异常,然后交给调用者去处理异常。runtime exception:指的是运行时异常,该类异常不必须本函数必须处理,当然也可以处理。八、值传递问题java中的方法传递都是值传递,java中的数据类型有基本类型和引用类型,他们都是值传递方式。基本类型传递的是它的值,因此方法中的改变参数的值,不会影响方法外。引用类型传递的是一个地址,因为引用类型在生成对象实例时,里面的值是一个地址,指向了对象实例。在传值的时候实际上传的是一个地址,他们指向了同一块地址,所以在方法内的改变会影响方法外的参数。 这里比较乱人心的是包

36、装类型和String,因为包装类型也是引用类型,这里应该就是和包装类型的实现有关了,在包装类型中,比如Integer a=1,有一个自动装箱的操作。其实a=1,如果现在令a=2,不会令2覆盖1(即1本身是不会变的),真正改变的是a被赋给了一个新地址,这个地址指向了2。因此方法内的改变包装类型的值就相当于改变了形参里面的地址,相当于重新new了一遍。而方法外面的实参仍旧指向含1的那个地址,以此方法内的改变不会影响方法外的实参。如果要修改必须指明this.str=10!。简单一点说的话,在Java里面只有基本类型和按照下面这种定义方式的String是按值传递,其它的都是按引用传递。就是直接使用双引

37、号定义字符串方式:String str = “Java”;九、常用名词的各种别称类方法就是静态方法;类变量就是静态变量;实例方法就是类的成员方法;实例变量指类中定义的变量,即类的成员变量,可以不进行初始化,Java会自动进行初始化,如果是引用类型默认初始化为null,如果是基本类型例如int则会默认初始化为0;在别的类中不可以直接使用。局部变量是定义在方法中的变量,必须要进行初始化,否则不同通过编译;常量是指被final修饰的变量。实例方法可以对当前对象的实例变量进行操作,也可以对类变量进行操作,但类方法不能访问实例变量。实例方法必须由实例对象来调用,而类方法除了可由实例对象调用外,还可以由类

38、名直接调用。另外,关于类方法的使用,有如下一些限制:1 在类方法中不能引用对象变量(不能访问非静态变量)。2 在类方法中不能使用super、this关键字。3 类方法不能调用类中的对象方法(不能调用实例方法)。十、线程局部存储TLS(Thread Local Storage)同一全局变量或者静态变量每个线程访问的是同一变量,多个线程同时访存同一全局变量或者静态变量时会导致冲突,尤其是多个线程同时需要修改这一变量时,通过TLS机制,为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可以独立地改变自己的副本,而不会和其它线程的副本冲突。 ThreadLocal可以给一个初始值,而每个

39、线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝。ThreadLocal 不是用于解决共享变量的问题的,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制。1、ThreadLocal的类声明:public class ThreadLocal可以看出ThreadLocal并没有继承自Thread,也没有实现Runnable接口。2、ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量。所以ThreadLocal重要作用并不在于多线程间的数据共享,而是数据的独立。ThreadLocal类代表一个线程局

40、部变量,通过把数据放在ThreadLocal中让每一个线程创建一个该变量的副本。由于每个线程在访问该变量时,读取和修改的,都是自己独有的那一份变量拷贝,不会被其他线程访问,变量被彻底封闭在每个访问的线程中。避免并发访问的线程安全问题。所以E对。3、ThreadLocal中定义了一个哈希表用于为每个线程都提供一个变量的副本。十一、内部类内部类其实和类的属性没什么区别,只是在声明的时候必须是Outer.Inner a,就像int a 一样。至于静态内部类和非静态内部类new的时候有点区别:Outer.Inner a=new Outer().new Inner()(非静态,先有Outer对象才能有属

41、性) Outer.Inner a=new Outer.Inner()(静态,要把Outer.Inner看成一部分,就像类变量一样)public class Enclosingone public class InsideOne /非静态内部类public static class InsideTwo/静态内部类classMytest02public static void main(String args )/非静态内部类对象Enclosingone.InsideOne obj1 =newEnclosingone().newInsideOne();/静态内部类对象Enclosingone.In

42、sideTwo obj2 =newEnclosingone.InsideTwo();十二、重载和重写方法重载的返回值的类型可以不同,同一个类中。因为判断方法重载的方法主要是根据方法的参数列表不同来判定;方法重写的返回值类型需要相同,重写就是子类继承了父类的方法,并在此方法上重写属于自己的特征,既然是继承过来的,那么它的返回值类型就必须要相同方法重载(overload): 重载是一个类中的多态性的一种表现1.必须是同一个类2方法名(也可以叫函数)一样3参数类型不一样或参数数量不一样方法的重写(override)两同两小一大原则: 重写是父类与子类之间多态性的一种表现方法名相同,参数类型相同子类返

43、回类型小于等于父类方法返回类型,子类抛出异常小于等于父类方法抛出异常,子类访问权限大于等于父类方法访问权限。十三、反射机制普通的java对象是通过new关键字把对应类的字节码文件加载到内存,然后创建该对象的。反射是通过一个名为Class的特殊类,用Class.forName(className);得到类的字节码对象,然后用newInstance()方法在虚拟机内部构造这个对象(针对无参构造函数)。也就是说反射机制让我们可以先拿到java类对应的字节码对象,然后动态的进行任何可能的操作。Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时

44、判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。public Method getDeclaredMethods()返回类或接口声明的所有方法,包括public, protected, default (package) 访问和private方法的Method对象,但不包括继承的方法。当然也包括它所实现接口的方法。public Method getMethods()返回某个类的所有public方法,包括其继承类的公用方法,当然也包括它所实现接口的方法。十四、关键字1、finalfinal可以修饰类、方法、属性!final不能修饰抽象类,因为抽象类一般都是需要

45、被继承的,final修饰类后就不能继承了,只能被实例化。final修饰的方法不能被重写,但可以重载!final修饰的变量内容是不能修改的(不允许再赋值),如果final修饰一个对象的引用,那么指的是这个对象的地址值是无法改变的,但对象的内容还是可以修改的。如果final修饰一个普通变量的话,就是变量在赋值之后就无法修改。综上,final修饰变量的本质就是:修饰的变量值(地址或内容)无法改变。final修饰的成员变量在赋值时可以有三种方式:1、在声明时直接赋值。2、在构造器中赋值。3、在初始代码块中进行赋值。final修饰对象的成员变量时,必须初始化;但是,如果不初始化,也可以在类的构造函数中初

46、始。对于形式参数只能用final修饰符,其它任何修饰符都会引起编译器错误。不过一般情况下,一个方法的形参不用final修饰。只有在特殊情况下,那就是:方法内部类。一个方法内的内部类如果使用了这个方法的参数或者局部变量的话,这个参数或局部变量应该是final。2、super和this1)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种无参形式的构造函数,那么在编译的时候就会报错。2)super()和this()类似,区别是,super从子类中调用父类的构造方法,this()在同一类内调用其它方法。3)su

47、per()和this()均需放在构造方法内第一行。4)尽管可以用this调用一个构造器,但却不能调用两个。5)this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。6)this和super都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。7)从本质上讲,this是一个指向本对象的指针,然而super是一个Java关键字。3、volatile一旦一个共享变量(类的成员变量、类

48、的静态成员变量)被volatile修饰之后,那么就具备了两层语义:1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。2)禁止进行指令重排序。volatile只提供了保证访问该变量时,每次都是从内存中读取最新值,并不会使用寄存器缓存该值每次都会从内存中读取。而对该变量的修改,volatile并不提供原子性的保证,也就不能保证线程安全。多线程下计数器必须使用锁保护。十五、SwingAWT是通过调用操作系统的native方法实现的,所以在Windows系统上的AWT窗口就是Windows的风格,而在Unix系统上的则是XWindow风格。

49、 AWT中的图形函数与操作系统所提供的图形函数之间有着一一对应的关系,我们把它称为peers。 也就是说,当我们利用 AWT 来构件图形用户界面的时候,我们实际上是在利用操作系统所提供的图形库。由于不同操作系统的图形库所提供的功能是不一样的,在一个平台上存在的功能在另外一个平台上则可能不存在。为了实现Java语言所宣称的一次编译,到处运行的概念,AWT不得不通过牺牲功能来实现其平台无关性,也就是说,AWT 所提供的图形功能是各种通用型操作系统所提供的图形功能的交集。由于AWT 是依靠本地方法来实现其功能的,我们通常把AWT控件称为重量级控件。Swing 是在AWT的基础上构建的一套新的图形界面

50、系统,它提供了AWT 所能够提供的所有功能,并且用纯粹的Java代码对AWT 的功能进行了大幅度的扩充,Swing是一个基于Java的跨平台MVC框架。使用单线程模式。AWT 是基于本地方法的C/C+程序,其运行速度比较快;Swing是基于AWT 的Java程序,其运行速度比较慢。Swing 是一个基于组件的框架,所有的Swing组件都是以JComponent类为起点的。尽管Swing库的某些部分并不以JComponent类为根,但所有的组件在其继承的某些级别上共享JComponent类作为通用父类。JComponent类定义通用的行为与属性。十六、抽象类和接口含有abstract修饰符的cl

51、ass即为抽象类。abstract类不能创建实例对象(不能实例化)。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。 接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static f

52、inal。 下面比较一下两者的语法区别: 1.抽象类可以有构造方法,接口中不能有构造方法。 2.抽象类中可以有普通成员变量,接口中没有普通成员变量 3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。 4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然 eclipse下不报错,但应该也不行),不能用private、static、synchronized、native访问修饰符修饰。但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。(java8中接口可以有default方法

53、) 5. 抽象类中可以包含静态方法,接口中不能包含静态方法(在jdk1.8中“接口中可以包含静态方法”,而jdk1.7中编译错误) 6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。 7. 一个类可以实现多个接口,但只能继承一个抽象类。 下面接着再说说两者在应用上的区别: 接口更多的是在系统架构设计方面发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用。例如,模板方法设计模式是抽象类的一个典型应

54、用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码。父类方法中间的某段代码不确定,留给子类干,就用模板方法设计模式。十七、构造器每个类中都有缺省构造器,不一定要定义构造器。构造器必须与类同名。而且类中的方法也可以和类同名,但和构造器区别在于类中的方法必须要有返回值类型。构造方法不能够有返回值,但可以用private修饰,单例模式就是使用private 来修饰构造方法的。构造函数添加参数是可以的,一个类可以定义多个参数列表不同的构造器,实现构造器重载。(注意只要类中有带参数的构造函数,程序不会加载默认的无参数的构造函数)Java中子类默认调用父类的无参构造函数。子类的构造方法总是先调用父类的构造方法,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类不带参数的构造方法。 而如果父类中没有无参的构造函数,那么子类需要在自己的构造函数中显示的调用父类的构造函数。初始化过程: 1. 初始化父类中的静态成员变量和静态代码块

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论