J2SE必须掌握的基础知识

来源:互联网 发布:古筝教学软件 编辑:程序博客网 时间:2024/05/17 09:33

1.    面向过程和面向对象

       面向过程

      优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

       缺点:没有面向对象易维护、易复用、易扩展

 

       面向对象

       优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

       缺点:性能比面向过程低

 

2.    封装

1. 实现对属性的数据访问限制,同时增加了程序的可维护性;

2. 隐藏方法和赋值实现细节,可以使我们容易地修改类的内部实现,而无需修改该类的客户端代码;

3. 可以对成员变量进行更精确的控制。

 

★不要编写返回可变对象的访问器方法

privateDate birthday; 

publicDate getBirthday() { 

   return birthday; 

publicvoid setBirthday(Date birthday) { 

    this.birthday = birthday; 

}

Date类型会破坏封装性,因为对象可变,

publicDate getBirthday() { 

    return (Date) birthday.clone(); 

}


3.    重载和重写的区别

重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。

重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private则子类中就不是重写。

 

4.    访问修饰符

Private:只有在本类中才能访问;

public:在任何地方都能访问;

protected:在同包内的类及包外的子类能访问;

default:默认不写在同包内能访问。


5.    多态

编译期多态:方法重载( Overload )

运行期多态:运行时,系统根据调用该方法的实例类型来决定调用哪个方法

 

多态存在的三个条件

继承、重写、父类引用指向子类对象

 

多态实现原理:

Fatherfather=new Son(); //多态     father.f1();

1.    方法区中的方法表中以数组形式记录了当前类以及其所有超类的可见方法字节码在内存中的直接地址,其中方法表中相同的方法位于相同的索引位置;

2.    invokevrtual指令:找到操作数栈顶<虚拟机栈中>第一个元素指向的对象的实际类型,记作C    father.f1()这是才会入栈

3.    若C中找到与常量中<方法区>描述符合简单名称都相符的方法,则进行访问权限校验,通过则返回这个方法的直接引用

4.    未找到则按照继承关系从下往上依次对C的各个父类进行查找和搜索

5.    查找完仍没有找到则抛出java.lang.AbstractMethodError


6.    接口和抽象类

语法层面:

1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;

2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面:

抽象类是对一种事物的抽象,即类的抽象

接口是对行为的抽象,是一种行为规范


7.    String相关

String对象是不可变的,String类中的每一个看起来会修改String值得方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象则丝毫未动。

String类的本质是字符数组char[],其次String类是final的,是不可被继承的,这点可能被大多数人忽略,再次String是特殊的封装类型,使用String时可以直接赋值,也可以用new来创建对象,但是这二者的实现机制是不同的。还有一个String池的概念,Java运行时维护一个String池,池中的String对象不可重复,没有创建,有则作罢。String池不属于堆和栈,而是属于常量池(方法区)。

当调用intern() 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String对象添加到池中,并且返回此 String 对象的引用。

题目见:http://blog.csdn.net/luotuomianyang/article/details/51804055

1.如果要操作少量的数据用  String

2.单线程操作字符串缓冲区下操作大量数据 = StringBuilder

3.多线程操作字符串缓冲区下操作大量数据 = StringBuffer(线程安全)

 

Equals和==

默认情况下也就是从超类Object继承而来的equals方法与‘==’是完全等价的,比较的都是对象的内存地址,但我们可以重写equals方法,使其按照我们的需求的方式进行比较,如String类重写了equals方法,使其比较的是字符的序列,而不再是内存地址。

 

Equals与hashcode()

这个问题主要是针对映射相关的操作,Map接口的类会使用到键对象的哈希码,当我们调用put方法或者get方法对Map容器进行操作时,都是根据键对象的哈希码来计算存储位置的,因此如果我们对哈希码的获取没有相关保证,就可能会得不到预期的结果。我们知道在Object类中,hashCode方法是通过Object对象的地址计算出来的,因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,如果一个类重写了equals方法,但没有重写hashCode方法。会导致两个对象通过equals方法是相等的,但是两个对象的hashcode方法返回值却不一样,这样的话,如果我们通过映射表(Map接口)操作相关对象时,就无法达到我们预期想要的效果。


8.    自动装箱和拆箱

装箱:将基本类型用它们对应的引用类型包装起来;

拆箱:将包装类型转换为基本数据类型;

Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率,由编译器来完成,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

Integer已经默认创建了数值【-128-127】的Integer缓存数据。所以使用Integer i1=40时,JVM会直接在该在对象池找到该值的引用。

 

Integer I = new Integer();  Integer I = x;的区别:

 

1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

http://blog.csdn.net/luotuomianyang/article/details/52217006

 

9.   泛型,类型擦除

Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。 泛型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。

类型擦除的主要过程如下:

一.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。

二.移除所有的类型参数。


10.    Final

Final修饰变量时,变量在运行时被初始化,值一直不会变;

Final修饰对象,一旦引用被初始化指向一个对象,则就无法把它在指向另一个对象,但是对象的本身可以修改;

Final修饰方法:在需要明确禁止覆盖的时候,选择把方法修饰成final的

Final修饰类:类不能被继承也不能被修改

 

Final与private

类中所有的private方法都隐式地指定为是final的,

 

11.    Static

当一个事物声明为static时,就意味着这个方法不会与包含它的那个类的任何对象实例关联在一起。

1.    只想为某特定域分配单一存储空间,而不考虑究竟能够创建多少对象,甚至就不创建对象;

2.    希望某个方法不与包含它的那个类的任何对象实例关联在一起;

Static修饰有两种访问方式:类名.filed   实例.filed

Static内部不能调用this


12.    类加载顺序

Father 静态块à Son 静态块àFather构造代码块àFather构造函数àSon构造代码块àSon构造函数

静态块:Static{  }    只会被加载一次,可以放置一次类加载变初始化的变量等等,

构造代码块   {  }   类实例化一次就加载一次如果有多个构造方法,可以把通用的步骤写在构造代码块里

变量初始化优于方法初始化;

 

13.    浅拷贝与深拷贝

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

深拷贝:深复制把要复制的对象所引用的对象都复制了一遍。

1.    派生类中实现Cloneable接口<标记接口>,覆盖clone接口,声明为public

2.    串行化:对象先实现Serializable接口,把对象写到流中,后读出

 

14.    Java新建对象实例的方法

1.    用new语句创建对象,这是最常用的创建对象的方式。

2.    运用反射手段,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。

3.    调用对象的clone()方法。

4.    运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.

 

15.    如何不借助第三个数实现x和y的数值交换

voidswap1(int &x, int &y)    

{  x=x+y;    y=x-y;  x=x-y;   }

 

16.    常见内存泄漏

无用对象持续占有内存得不到及时释放,造成内存空间的浪费

1.    静态集合类引起内存泄露

比如典型的ThreadLocal导致的内存泄漏

http://blog.csdn.net/luotuomianyang/article/details/52263653

2.    监听器:释放对象的时候没有释放对应对象的监听器

3.    数据库连接等:忘记关闭

4.    单例:单例对象在被初始化后将在JVM的整个生命周期中存在,如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露,


17. 序列化

         Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。

为什么需要:

        我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

 当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

如何实现: 实现Serializable接口

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("my.out"));//输出流保存的文件名为 my.out                                                                         //ObjectOutputStream能把Object输出成Byte流MyTest myTest=new MyTest();oos.writeObject(myTest); oos.flush();  //缓冲流 oos.close(); //关闭流

如何防止某个属性被序列化:使用transient关键字修饰


0 0
原创粉丝点击