当年看着挺装逼的那股风。。。just so,so

在工作与成效的关系上,一个人肯干事是态度、想干事是热情、会干事是能力、干成事才是本事,这种本事靠上级封不出来,靠权力压不出来、靠自己吹不出来,只有靠实实在在做人、认认真真做事,才能逐步得到提高。

当年看着挺装逼的java类

1.JFrame frame = new JFrame("JFrame演示");//窗口框
2.JPanel panel = new JPanel();//面板
3.JLabel label = new JLabel("hello words");//文字标签
4.frame.setContentPane(panel);//窗口框设置内容面板是panel
5.frame.setSize(500,500);//设置大小
6.frame.setVisible(true);//窗口框设置可见性为真
7.Random random =new Random();//产生随机数
8.Scanner scanner = new Scanner(System.in);// 控制台接受输入的下一行字
String str = scanner.nextLine();// 从控制台输入一行字并处理
9.Scanner scanner = new Scanner(System.in);//控制台输入
double doub = scanner.nextDouble();//转化成double型的
doub = Math.pow(doub, 5);//计算一个数的平方

转载当年不知道在哪里看到的挺装逼的Java基础,强行解释一波。

代理是一种常用的程序设计模式,如同网络代理一样,代理是介于调用者和真正调用目标对象之间的中间对象,代理在调用真正目标对象时提供一些额外或者不同的操作,真正的对目标对象的操作还是通过代理调用目标对象来完成。

简单的代理例子如下:
[java] view plaincopy

//接口
interface Interface{
void doSomething();
void somethingElse(String arg);
}
//目标对象
class RealObject implement Interface{
public void doSomething(){
System.out.println(“RealObject doSomething”);
}
public void somethingElse(String arg){
System.out.println(“RealObject somethingElse ” + arg);
}
}
//简单代理对象
class SimpleProxy implements Interface(
private Interface proxied;
public SimpleProxy(Interface proxied){
this.proxied = proxied;
}
public void doSomething(){
System.out.println(“SimpleProxy doSomething”);
proxied.doSomething();
}
public void somethingElse(String arg){
System.out.println(“SimpleProxy somethingElse ” + arg);
proxied.somethingElse(arg);
}
)
Class SimpleProxyDemo{
public static void consumer(Interface iface){
iface.doSomething();
iface.somethingElse(“TestProxy”);
}
public static void main(String[] args){
//不是用代理
cosumer(new RealObject());
//使用代理
cosumer(new SimpleProxy(new RealObject()));
}
}


输出结果为:

RealObject doSomething

RealObjectsomethingElse TestProxy

SimpleProxy doSomething

RealObject doSomething

SimpleProxy somethingElse TestProxy

RealObject somethingElse TestProxy

上面例子可以看出代理SimpleProxy在调用目标对象目标方法之前做了一些额外的操作。

Java中的代理是针对接口的动态代理,当然java也可以使用第三方的CGLIB实现针对类的代理,但是JDK中只支持针对接口的动态代理,我们只分析JDK的动态代理。

JDK动态代理的要素:

(1).实现了InvocationHandler的代理处理类,实现其invoke方法,该方法是代理调用目标对象方法以及提供额外操作的方法。

(2).使用Proxy.newProxyInstance(类加载器, 代理接口列表,InvocationHandler对象);方法创建实现了指定接口的动态代理。

JDK的代理例子如下:
[java] view plaincopy

//接口
interface Interface{
void doSomething();
void somethingElse(String arg);
}
//目标对象
class RealObject implement Interface{
public void doSomething(){
System.out.println(“RealObject doSomething”);
}
public void somethingElse(String arg){
System.out.println(“RealObject somethingElse ” + arg);
}
}
//代理处理类
class DynamicProxyHandler implements InvocationHandler{
provate Object proxied;
public DynamicProxyHandler(Object proxied){
this.proxied = proxied;
}
//动态代理调用目标对象的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println(“Dynamic proxy invoke”);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy{
public static void consumer(Interface iface){
iface.doSomething();
iface.somethingElse(“DynamicProxy”);
}
public static void main(String[] args){
RealObject real = new RealObject();
//不是用代理
consumer(real);
//创建动态代理
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{Interface.class},
new DynamicProxyHandler(real)
);
cosumer(proxy);
}
}

输出结果为:

RealObject doSomething

RealObject somethingElse DynamicProxy

Dynamic proxy invoke

RealObject doSomething

Dynamic proxyinvoke

RealObject somethingElse DynamicProxy

转载当年不知道在哪里看到的挺装逼的Java基础,强行解释第二波。

1.集合中添加另一个集合的方法:

(1).Collection.addAll(被添加的Collection对象)方法:

如:list1.addAll(list2);

(2).Collections.addAll(添加到的目标Collection对象,可变参数的集合或者对象)方法:

如:Collections.addAll(list1, new Object1(), new Object2()…);

Collectionns.addAll(list1, list2…);

注意:Collections是java集合容器的工具类,相比于(1),使用Collections的(2)更灵活。

2.Java集合中常用的交集、并集和差集操作:

并集:collection对象1.addAll(collection对象2);

交集:collection对象1. retainAll(collection对象2);

差集:collection对象1. removeAll(collection对象2);

注意:上述的集合操作时,集合元素的equals方法会影响操作结果。

3.将其他类型集合转换为List:

Arrays.asList(非List类型的集合对象/可变参数的对象);方法可以将传递的参数转变为List集合。如:Arrays.asList(new Object1(),new Object2(),…);

Arrays和Collections类似,是Array数组类型集合的工具类。

注意:Arrays.asList()方法转换后的List对象是一个size不能改变的对象,如果对该对象做增加或者删除元素操作时,将会报不支持的操作异常。

4.List集合:

List集合主要有两种具体的集合容器:ArrayList和LinkedList。

(1).ArrayList:底层实现是数组,提供了根据数组下标快速随机访问的能力,但是增加和删除元素时因为需要引动数组的元素,因此比较慢。

(2).LinkedList:底层实现是链表,链表访问元素时必须从链表头至链表尾挨个查找,因此只能顺序访问,速度比随机访问要慢。但是增加和删除元素时,只需要修改链表的指针而不需要移动元素,因此速度比较快。

5.LinkedList:

LinkedList除了实现了基本的List接口以外,还提供了一些特定的方法,使得LinkedList可以方便地实现Stack、Queue以及双端Queue的功能。

LinkedList提供的非List接口方法:

(1).getFirst():获取并且不移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。

(2).element():获取并且不移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。

(3).peek():获取并且不移除LinkedList集合中第一个元素。如果集合为空,则返回null。

(4).removeFirst():获取并且移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。

(5).remove():获取并且移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。

(6).poll():获取并且移除LinkedList集合中第一个元素。如果集合为空,则返回null。

(7).addFirst():向LinkedList集合的头部插入一个元素。

(8).add():向LinkedList集合的尾部插入一个元素。

(9).offer():向LinkedList集合的尾部插入一个元素。

(10).removeLast():获取并且移除LinkedList集合中最后一个元素。如果集合为空,抛出NoSuchElementException异常。

6.Iterator:

Iterator迭代器在java集合容器中应用比较广泛,对于List类型的集合,可以通过下标索引值获取到指定的元素,而对于Set类型的集合,因为Set是没有索引的,因此只能通过迭代器来遍历。

Iterator迭代器是一个顺序选择和遍历集合元素的对象,使用者不需要关心其底层的数据结构和实现方式。Java中的Iterator迭代器是单向的。

Iterator的常用方法如下:

(1).collection对象.iterator()方法:将集合对象转换为Iterator迭代器。

(2).iterator对象.hasNext()方法:判断迭代器中是否还有元素。

(3).iterator对象.next()方法:获取迭代器中下一个元素。
(4).iterator对象.remove()方法:删除迭代器中当前元素。

注意:使用迭代器的好处是,当数据结构从List变为Set之后,迭代集合的相关代码一点都不用改变。

7.ListIterator:

ListIterator是Iterator的子类,它只能有List类型的集合产生,ListIterator是一个双向的迭代器,即它可以向前和向后双向遍历集合。ListIterator的常用方法如下:

(1).list类型对象.listIterator():将List类型的集合转换为ListIterator迭代器。

(2).list类型对象.listIterator(int n):将List类型的集合转换为ListIterator迭代器,同时指定迭代器的起始元素为第n个元素。

(3).listIterator对象.hasNext():判断迭代器中是否还有下一个元素。

(4).listIterator对象.next():获取迭代器中的下一个元素。

(5).listIterator对象.hasPrevious():判断迭代器中是否还有前一个元素。

(6).listIterator对象.previous():获取迭代器中的前一个元素。

(7).listIterator对象.set(元素对象):将当前迭代到的元素设置为另一个值。

8.Map遍历3中方法:

Map<String, Object>map = new HashMap<String, Object>();

map.put(“test1”, object1);

……

map.put(“testn” , objectn);

(1).Map的values()方法可以获取Map值的集合:

[java] view plaincopy

Iterator it = map.values().iterator();
while(it.hasNext()){
Object obj = it.next();
}


(2).Map的keySet方法可以获取Map键的Set集合:
[java] view plaincopy

Set<String> keys = map.keySet();
for(Iterator it = key.iterator(); it.hasNext(); ){
String key = it.next();
Object obj = map.get(key);
}


(3).通过使用Entry来得到Map的key和value:
[java] view plaincopy

Set<Map.Entry<String, Object>> entrySet = map.entrySet();
for(Iterator <Map.Entry<String, Object>> it = entrySet.iterator(); it.hasNext(); ){
Map.Entry<String, Object> entry = it.next();
String key = entry.getKey();
Object value = entry.getValue();
}


9.Collection和Iterator:

Collection是java中除了Map以外的集合容器的通用接口,如果想自定义一种集合容器类型的类,可以选择实现Collection接口或者继承Collection的子类。

实现Collection接口或者继承Collection子类的时候,必须实现Collection接口的所有方法,而Iterator为定义自定义集合容器类型提供了另一种方便,Iterator是一种轻量级的接口,只需要实现hasNext(),next()方法即可,remove()方法是可选方法。

注意:Foreach循环支持所有实现了Iterable接口的集合容器(Collection接口的父接口是Iterable),Map集合没有实现Iterable接口,因此不支持Foreach循环。

转载当年不知道在哪里看到的挺装逼的Java基础,强行解释三波。

1.java中的4种访问制权限:

(1).public:最大访问控制权限,对所有的类都可见。

(2).protect:同一包可见,不在同一个包的所有子类也可见。

(3).default:包访问权限,即同一个包中的类可以可见。默认不显式指定访问控制权限时就是default包访问控制权限。

(4).private:最严格俄访问控制权限,仅该类本身可见,对外一切类都不可以访问(反射机制可以访问)。

2.面向对象编程中两种对象组合方式——is-a 和 has-a:

(1).is-a组合:一个类继承具有相似功能的另一个类,根据需要在所继承的类基础上进行扩展。

优点:具有共同属性和方法的类可以将共享信息抽象到父类中,增强代码复用性,同时也是多态的基础。

缺点:子类中扩展的部分对父类不可见,另外如果共性比较少的时候使用继承会增加冗余代码。

(2).has-a组合:has-a组合是在一个类中引用另一个类作为其成员变量。

优点:可扩展性和灵活性高。在对象组合关系中应优先考虑has-a组合关系。

缺点:具有共性的类之间看不到派生关系。

3.多态:

在面向对象编程中,子类中拥有和父类相同方法签名的方法称为子类方法覆盖父类方法,当调用子类方法的某个操作时,不必明确知道子类的具体类型,只需要将子类类型看作是父类的引用调用其操作方法,在运行时,JVM会根据引用对象的具体子类类型而调用应该的方法,这就是多态。

多态的基础是java面向对象编程的晚绑定机制。编程中有如下两种绑定机制:

(1).早绑定:一般在非面向对象编程语言中使用,在程序编译时即计算出具体调用方法体的内存地址。

(2).晚绑定:面向对象编程语言中经常使用,在程序编译时无法计算出具体调用方法体的内存地址,只进行方法参数类型和返回值类型的校验,在运行时才能确定具体要调用方法体的内存地址。

4.java单继承的优点:

相比于C++的多继承,java只支持类的单继承,java中的所有类的共同基类是Object类,Object类java类树的唯一根节点,这种单继承有以下好处:

(1).单继承可以确保所有的对象拥有某种共同的特性,这样对于JVM虚拟机对所有的类进行系统级的操作将提供方便,所有的java对象可以方便地在内存堆栈中创建,传递参数也变的更加方便简单。

(2).java的单继承使得实现垃圾回收器功能更加容易,因为可以确保JVM知道所有对象的类型信息。

5.选择容器对象两个原则:

(1).容器所能提供不同的类型的接口和外部行为是否能够满足需求。

(2).不同容器针对不同的操作效率不同。

6.类型转换:

Java中有两种常见的类型转换:向上类型转换(upcast)和向下类型转换(downcast):

(1).向上类型转换(upcast):

向上类型转换是将子类对象强制类型转换为父类类型,经典用法是面向对象的多态特性。向上类型转换时,子类对象的特性将不可见,只有子类从父类继承的特性仍然保持可见,向上类型转换时编译器会自动检查是否类型兼容,通常是安全的。

(2).向下类型转换:

向下类型转换是将父类类型强制转换为子类类型,转换过后父类中不可见的子类特性又恢复可见性,向下类型转换时,编译器无法自动检测是否类型兼容,往往会产生类型转换错误的运行时异常,通常不安全。

7.java中5个存放数据的地方:

(1).寄存器(Registers):位于CPU内部,是速度最快的存储区,但是数量和容量有限。在java中不能直接操作寄存器。

(2).栈(Stack):栈位于通用随机访问存储器 (General random-access memory,RAM,内存) 中,通过处理器的栈指针访问,栈指针从栈顶向栈底分配内存,从栈底向栈顶释放内存。栈是仅次于寄存器的速度第二快的存储器,在java程序中,一般的8种基本类型数据和对象的引用通常存放在栈内存中,不通过new关键字的字符串对象也是存放在栈的字符串池中。栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

(3).堆(Heap):也是位于通用随机访问存储器 (General random-access memory,RAM,内存) 中的共享内存池。Java的堆是一个运行时数据区,类的对象从中分配空间,凡是通过new关键字创建的对象都存放在堆内存中,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

(4).常量存储器(Constant storage):java中的常量是存放在系统内嵌的只读存储器中(read-only memory,ROM)的。

(5).非随机存储器(Non-RAM storage):对于流对象和持久化对象,通常存放在程序外的存储器,如硬盘。
8.移位运算:

左移运算符<<:将比特位左移指定位数,右边部分补0,左移一位相当于乘2。

右移运算符>>:将比特位右移指定位数,如果是正数,左边第一位(符号位)补0,其余位补0,如果是负数,左边第一位补1,其余位补0。右移一位相当于除2。

无符号右移运算符>>>:将比特位右移指定位数,不论是正数或者负数,左边移除位统统补0。

9.java中,比int类型小的原始类型(char、byte、short)进行数学运算或者位运算时,数据类型首先转换成int类型,然后进行相应的运算。

10.方法重载(overloading):方法同名,参数列表不同称为方法重载,注意方法的返回值类型不同不能作为方法重载。

11.java中的析构函数:

Java中没有像C/C++的析构函数,用来销毁不用的对象是否内存空间,只有以下三个方法用于通知垃圾回收器回收对象。

(1).finalize( )只是通知JVM的垃圾收集器当前的对象不再使用可以被回收了,但是垃圾回收器根据内存使用状况来决定是否回收。

finalize()最有用的地方是在JNI调用本地方法时(C/C++方法),调用本地方法的析构函数消耗对象释放函数。

(2). System.gc()是强制析构,显式通知垃圾回收器释放内存,但是垃圾回收器也不一定会立即执行,垃圾回收器根据当前内存使用状况和对象的生命周期自行决定是否回收。

(3).RunTime.getRunTime().gc()和System.gc()类似。

注意:这三个函数都不能保证垃圾回收器立即执行,推荐不要频繁使用。

12.垃圾回收器原理:

(1).引用计数(ReferenceCounting)垃圾回收算法:

一种简单但是速度较慢的垃圾回收算法,每个对象拥有一个引用计数器(Reference Counter),当每次引用附加到这个对象时,对象的引用计数器加1。当每次引用超出作用范围或者被设置为null时,对象的引用计数器减1。垃圾回收器遍历整个对象列表,当发现一个对象的引用计数器为0时,将该对象移出内存释放。

引用计数算法的缺点是,当对象环状相互引用时,对象的引用计数器总不为0,要想回收这些对象需要额外的处理。

引用计数算法只是用来解释垃圾回收器的工作原理,没有JVM使用它实现垃圾回收器。

引用计数的改进算法:

任何存活的对象必须被在静态存储区或者栈(Stack)中的引用所引用,因此当遍历全部静态存储区或栈中的引用时,即可以确定所有存活的对象。每当遍历一个引用时,检查该引用所指向的对象,同时检查该对象上的所有引用,没有引用指向的对象和相互自引用的对象将被垃圾回收器回收。

(2).暂停复制(stop-and-copy)算法:

垃圾回收器的收集机制基于:任何一个存活的对象必须要被一个存储在栈或者静态存储区的引用所引用。

暂停复制的算法是:程序在运行过程中首先暂停执行,把每个存活的对象从一个堆复制到另一个堆中,已经不再被使用的对象被回收而不再复制。

暂停复制算法有两个问题:

a.必须要同时维护分离的两个堆,需要程序运行所需两倍的内存空间。JVM的解决办法是在内存块中分配堆空间,复制时简单地从一个内存块复制到另一个内存块。

b.第二个问题是复制过程的本身处理,当程序运行稳定以后,只会产生很少的垃圾对象需要回收,如果垃圾回收器还是频繁地复制存活对象是非常低性能的。JVM的解决方法是使用一种新的垃圾回收算法——标记清除(mark-and-sweep)。

一般来说标记清除算法在正常的使用场景中速度比较慢,但是当程序只产生很少的垃圾对象需要回收时,该算法就非常的高效。

(3).标记清除(mark-and-sweep)算法:

和暂停复制的逻辑类似,标记清除算法从栈和静态存储区开始追踪所有引用寻找存活的对象,当每次找到一个存活的对象时,对象被设置一个标记并且不被回收,当标记过程完成后,清除不用的死对象,释放内存空间。

标记清除算法不需要复制对象,所有的标记和清除工作在一个内存堆中完成。

注意:SUN的文档中说JVM的垃圾回收器是一个后台运行的低优先级进程,但是在早期版本的JVM中并不是这样实现的,当内存不够用时,垃圾回收器先暂停程序运行,然后进行垃圾回收。

(4).分代复制(generation-copy)算法:

一种对暂停复制算法的改进,JVM分配内存是按块分配的,当创建一个大对象时,需要占用一块内存空间,严格的暂停复制算法在释放老内存堆之前要求把每个存活的对象从源堆拷贝到新堆,这样做非常的消耗内存。

通过内存堆,垃圾回收器可以将对象拷贝到回收对象的内存堆中,每个内存块拥有一个世代计数(generation count)用于标记对象是否存活。每个内存块通过对象被引用获得世代计数,一般情况下只有当最老的内存块被回收时才会创建新的内存块,这主要用于处理大量的短存活周期临时对象回收问题。一次完整的清理过程中,内存块中的大对象不会被复制,只是根据引用重新获得世代计数。

JVM监控垃圾回收器的效率,当发现所有的对象都是长时间存活时,JVM将垃圾回收器的收集算法调整为标记清除,当内存堆变得零散碎片时,JVM又重新将垃圾回收器的算法切换会暂停复制,这就是JVM的自适应分代暂停复制标记清除垃圾回收算法的思想。

13.java即时编译技术(JIT):

Java的JIT是just-in-timecomplier技术,JIT技术是java代码部分地或全部转换成本地机器码程序,不再需要JVM解释,执行速度更快。

当一个”.class”的类文件被找到时,类文件的字节码被调入内存中,这时JIT编译器编译字节码代码。

JIT有两个不足:

(1).JIT编译转换需要花费一些时间,这些时间贯穿于程序的整个生命周期。

(2).JIT增加了可执行代码的size,相比于压缩的字节码,JIT代码扩展了代码的size,这有可能引起内存分页,进而降低程序执行速度。

对JIT不足的一种改进技术是延迟评估(lazy evaluation):其基本原理是字节码并不立即进行JIT编译除非必要,在最近的JDK中采用了一种类似延迟JIT的HotSpot方法对每次执行的代码进行优化,代码执行次数越多,速度越快。

14.非内部类的访问控制权限只能是默认的包访问权限或者是public的,不能是protected和private的。内部类的访问控制权限可以是protected和private。

15.Java中的高精度数值类型:

BigInteger和BigDecimal是java中的高精度数值类型,由于它们是用于包装java的基本数据类型,因此这两个高精度数值类型没有对应的原始类型。

(1).BigInteger支持任意精度的整数,即使用BigInteger可以表示任意长度的整数值而在运算中不会因为范围溢出丢失信息。

(2).BigDecimal支持任意精度的固定位数浮点数,可以用来精确计算货币等数值。

普通的float和double型浮点数因为受到小数点位数限制,在运算时不能准确比较,只能以误差范围确定是否相等,而BigDecimal就可以支持固定位数的浮点数并进行精确计算。

16.Java只处理public和protected访问控制权限成员的文档注释,private和默认的包访问控制权限成员的文档注释将被忽略。

17.Java中赋值运算:

Java中赋值运算是把赋值运算符”=”右边的值简称右值拷贝到赋值运算符左边的变量,如a=b,即把b代表的变量或常量值复制给变量a,切记a只能是变量,不能说常量值。

(1).原始类型赋值运算:

Java中8种原始数据类型赋值运算是将赋值运算符右边的值拷贝到赋值运算符左边的变量中。

原始类型赋值运算后,无论改变赋值运算符那一边的值,都不会影响赋值运算符另一边的值。

(2).引用类型的赋值运算:

Java中除了8中原始数据类型外,所有的数据类型都是对象类型,对象类型的赋值运算是操作引用,如a=b,把b引用赋值给a引用,即原本b引用指向的对象现在由a和b引用同时指向。

引用赋值运算符又叫别名运算符,即它相当于给引用对象取了一个别名,其实引用的还是同一个对象。

引用类型的赋值运算,如果赋值运算符任意一边的引用改变了被引用对象的值,赋值运算符另一边的引用也会受影响,因为两个引用指向的是同一个被引用的对象。

转载当年不知道在哪里看到的挺装逼的Java基础,强行解释四波。

1.java中,可以将一个类的定义放在另一个类的内部,这种叫做内部类。

内部类允许编程人员将逻辑上相关的类组织在一起,并且控制内部类对其他类的可见性。

2.在外部类的非静态方法中创建内部类的对象语法:

外部类类名.内部类类名 对象名 = 外部类对象.new 内部类类名();

如:
[java] view plaincopy

public class Outter{
class inner{
}
}
Outter out = new Outter();
Outter.Inner inner = out.new Inner();


注意:非静态的内部类必须要有外部类对象之后才能创建,因为外部类对象持有内部类的引用,如果内部类是静态的,则不需要外部类对象引用内部类对象。

3.内部类对外部类对象的引用:

外部类中所有的元素对内部类都是可见的,内部类持有对外部类对象引用的语法:外部类名称.this。如:
[java] view plaincopy

public class Outter{
void f(){
System.out.println(“Outter f() method”);
}
class Inner{
public Outter outer(){
return Outter.this;
}
}
public Inner inner(){
return new Inner();
}
}


4.方法内部类:

除了最常见的最为成员变量的内部类以外,内部类还可以定义在方法中,如:
[java] view plaincopy

public class Outter{
public Inner getInner(){
class Inner{
public void f(){
System.out.println(“Method inner class”);
}
}
return new Inner();
}
public void fout(){
Inner inner = getInner();
inner.f();
}
}


5.匿名内部类:

Java中匿名内部类的应用十分广泛,所谓的匿名内部类就是指所创建的内部类没有类名称,也就是不知道内部类的具体类型,如:
[java] view plaincopy

public class Outter{
public Inner getInner(){
return new Inner(){
private String name = “inner”;
public String getName(){
return name;
}
};
}
}


上面的return newInner{……};就是一个匿名内部类,这个匿名内部类继承了Inner类,即其基类是Inner,但是其具体类型不清楚,该匿名内部类等效于下面的内部类写法:
[java] view plaincopy

public class Outter{
class MyInner implement Inner{
private String name = “Inner class”;
public String getName(){
return name;
}
}
public Inner getInner(){
return new MyInner();
}
}


注意:匿名内部类是实现了new关键字之后的接口或继承了类,只是没有具体的类名称。

6.匿名内部类传递final参数:

如果在创建匿名内部类,需要外部类传递参数时,参数必须是final类型的,否则,编译时会报错。如:
[java] view plaincopy

public class Outter{
public Inner getInner(final String name){
return new Inner(){
public name getName(){
return name;
}
};
}
}


注意:如果外部类传递的参数在内部类中使用,则必须是final类型,如果没有在内部类中使用(如,仅在基类中使用),则可以不用是final类型的。

7.静态内部类:

静态内部类又称为嵌套类,静态内部类和普通内部类的区别:

(1).对于非静态的内部类来说,内部类和外部类必须保持对象引用,内部类可以访问外部类的任何元素.

(2).静态内部类不需要和外部类保持对象引用,静态内部类只能访问外部类的静态元素。

8.为什么要使用内部类:

使用内部类主要有以下两个原因:

(1).解决java中类不能多继承的问题:

Java中的继承是单继承,在某些情况下接口的多继承可以解决大部分类似C++中多继承的问题,但是如果一个类需要继承两个父类而不是接口,在java中是没法实现这种功能的,内部类可以帮助我们部分解决这种问题,如:
[java] view plaincopy

abrstact class A{
abstract public void f();
}
abstract class B{
abstract public void g();
}
public class D extends A{
public void f(){}
public B makeB(){
return new B(){
public void g(){};
}
}
}


这样既继承了A类,有在makeB方法中使用匿名内部类继承了B类。

(2).闭包方法问题:

在面向对象中有个术语叫闭包,所谓闭包是指一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。Java中不支持闭包,但是java的内部类可以看作是对闭包的一种解决方案,因为外部类的所有元素对于普通的内部类来说都是可见的,并且内部类还包含一个指向外部类的对象引用,因此在作用域内,内部类有权操作外部类的所有成员包括private成员。如:

[java] view plaincopy

interface Incrementable {
void increment();
}

class Callee1 implements Incrementable {
private int i=0;
public void increment() {
i++;
System.out.println(i);
}
}

class MyIncrement {
void increment() {
System.out.println("other increment");
}
static void f(MyIncrement mi) {
mi.increment();
}
}

class Callee2 extends MyIncrement {
private int i=0;
private void incr() {
i++;
System.out.println(i);
}
//闭包内部类
private class Closure implements Incrementable {
public void increment() {
incr();
}
}
//回调函数
Incrementable getCallbackReference() {
//新建内部类
return new Closure();
}
}

class Caller {
private Incrementable callbackRefference;
Caller(Incrementable cbh) {
callbackRefference = cbh;
}
void go() {
//调用increment()方法
callbackRefference.increment();
}
}

public class Callbacks {
public static void main(String [] args) {
Callee1 c1=new Callee1();
Callee2 c2=new Callee2();
MyIncrement.f(c2);
Caller caller1 =new Caller(c1);
//将内部类中的Closure赋给Caller
Caller caller2=new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}

输出:
other increment
1
2
1
2
Callee2 通过使用内名内部类提供了一个访问自身increment方法的钩子,
通过getCallbackReference()方法获取内部类对象的引用,进而通过内部类的increment方法调用外部类的回调方法,这就是java中用于实现回调方法的闭包。

转载当年不知道在哪里看到的挺装逼的Java基础,强行解释五波。

1.java类的初始化顺序:

(1).在一个类中,初始化顺序由变量在类中的声明定义顺序决定,成员变量(非set方法和构造方法的初始化)的初始化发生在方法调用之前,包括构造方法。

(2).静态变量在整个存储区只保留一份拷贝,本地变量不能使用静态关键字,基本类型的静态变量不需要初始化,它会根据类型获得初始化值,引用类型的静态变量默认初始化为null。

静态变量的初始化发生在需要使用的时候,一旦被初始化之后,静态变量就不会再初始化。

(3).静态初始化块和静态变量类似的执行也在构造方法之前,并且仅执行一次。

(4).动态初始化块(与静态初始化块类似,只是没有static关键字,即放在一对大括号中的代码块)在静态初始化块初始化结束后执行,动态初始化块每次创建新对象都会初始化一次。

(5).构造方法执行时,先执行父类的构造方法,后执行子类的构造方法。

(6).本地变量初始化最晚,在方法中初始化。

综述,类的初始化顺序依次为:

a.父类的静态变量/静态初始化块;

b.子类类的静态变量/静态初始化块;

c.父类的动态初始化块、非构造方法和set方法的成员变量初始化

d.子类的动态初始化块、非构造方法和set方法的成员变量初始化

e.父类的构造方法。

f.子类的构造方法。

g.父类本地变量。

h.子类的本地变量。

2.数组初始化:

Java中数组初始化有以下3中方式:

(1).数组声明时直接初始化,如:

int[] a = {1,2,3};

(2).动态数组初始化,如:

int[] a = new int[]{1,2,3};

注意:动态数组初始化时,不能在new()操作符中指定数组的大小,即int[] a = new int[3]{1,2,3}的写法是错误的,数组的大小由初始化数组元素个数决定。

(3).固定长度数组初始化,如:

int[] a = new int[3];

a[1] = 0;

a[2] = 1;

a[3] = 2;

注意:固定长度大小的数组初始化时不能大于所声明的数组长度,没有声明的数组元素使用其默认值,如int默认为0,对象类型的值为引用,默认为null.

3.java代码重用4中方式:

java面向对象编程中提供了如下4中代码重用的方式:

(1).组合:

面向对象编程中最常用的代码复用方式,具体的方式是在一个对象中将另一个对象引用最为成员变量,其最大的优点是既实现松散耦合,有可能提高代码复用率。

(2).继承:

面向对象编程中常用的提高代码复用率的方法之一,适用于子类和父类是同一种抽象类型,具有共同的属性情况。

使用继承,子类可以复用父类除private私有房屋控制权限以为的所有属性和方法,编译器将父类封装为子类对象内部的一个对象。

需要注意的是:调用子类初始化构造方法时,编译器会确保首先调用父类的构造方法初始化父类,然后才初始化子类,如果父类中没有默认的构造方法,即需要显式传入参数的构造方法时,子类必须通过super关键字显式传入参数调用父类的构造方法。

(3).委派:

Java中不支持委派方式的代码复用,但是开发人员可以使用委派机制实现代码的重用。

委派是指,java对象的所有方法其实都是委派调用另一个类的方法实现,但是当前类又不是所委派类的类型,因此使用继承不太合适,解决方式和组合类似,将被委派类作为委派类的成员变量,委派类的方法直接调用被委派类对象应用的方法。

如:
[java] view plaincopy

//委派类
public Class A{
//被委派类
private B b = new Class B();
public void method1(){
b.method1();
}
public void method2(){
b.method2();
}
……
}


(4).联合使用组合和继承方式:

因为java中不允许多继承,如果某种情况下,一个java类需要使用多个其他类功能,且该类和其中某个类具有很多共同属性,即可以看作同一类,则可以使当前类继承具体共同属性的类,同时将其他类作为成员变量组合引用。

4.组合和继承的区别:

组合和继承都可以复用代码,很多java程序员,甚至是架构师都分不清楚什么情况下该使用组合,什么情况下应该使用继承,具体的区别如下:

(1).组合:

组合通常是在一个类中想使用另一个类已有的特性,但是却不想使用其接口。

使用组合可以可以将一个类作为当前类的内嵌对象,这样在当前类中就可以显式地使用内嵌类已经实现的功能,与此同时又不会影响当前类的调用接口。

(2).继承:

继承是隐式地使用被继承类的功能,相当于提供了一个新版本的父类实现。

使用继承,子类不但可以复用父类的功能,同时还复用了父类的接口,子类和父类的对外调用接口相同的情况下适合使用继承。

使用继承时,很多情况下需要向上类型转换,即将子类看作其父类。在编程时到底选用组合方式还是继承方式,一个简单的判断依据是:是否需要向上类型转换,如果需要就使用继承,如果不需要,则选择组合。

5.final方法:

Java中使用final类型的方法有以下两种原因:

(1).设计原因:

final类型的方法不允许其子类修改方法,即不允许子类覆盖父类的final方法。

(2).效率原因:

在早期的java实现中,如果方法被声明为final,编译器将final方法调用编译为内联调用。

正常的方法调用是:如果方法调用时,将当前的方法上下文保持到栈中,调用被调用的方法,然后在将调用上下文出栈恢复调用现场。

内联调用是:如果方法调用时,将被调用方法体拷贝到当前调用的地方合并成一个方法体,这样就避免因需要保存方法调用线程而进行的进栈和出栈操作,可以提高效率。

新版的使用hotspot技术的java虚拟机可以探测方法调用情况而做效率优化,final方法不再作为提高效率的手段,唯一的作用是确保方法不被子类覆盖。

注意:任何private的方法都是隐式的final类型,同final方法类似,private方法不能被子类所覆盖,但是private比final更严格,基类的private方法对子类不可见,private方法不再是接口的一部分。

6.多态性:

面向对象编程中的多态和继承往往是一起发挥作用的,使用继承,所有的子类和父类使用相同的对外接口,而多态的基础是晚绑定或动态绑定或运行时绑定,即对象引用使用基类类型,在编译时编译器无法确切知道到底调用哪一个具体类,只有在运行时,java虚拟机才通过类型检查确定调用对象的具体类型。

Java中默认对象引用全部是晚绑定,只有static和final类型的引用时早绑定或编译时绑定。

多态的优势是:程序的可扩张性好,无论添加多少个子类,基类的接口都不用改变,只需要在子类对应方法中提供具体实现即可,也就是所谓的将程序变化的部分和程序保持不变的部分分离。

注意:只有正常的方法可以使用多态,字段和静态方法没有多态机制。

构造方法也不支持多态机制,构造方法是隐式的static声明。

posted @ 2018-01-31 15:31  用什么暖你一千岁!  阅读(196)  评论(0)    收藏  举报