自己最近温习了一下Java ,并总结了一些Java中比较基础的核心知识,之前也写过一篇Java基础知识(http://blog.csdn.net/hqocshheqing/article/details/49464135),帮助自己巩固这些知识,也分享给大家,希望能帮助需要的人:
- 重载(Overload)和覆写(Override)
重载:是指在同一个类内定义名称相同但参数个数或类型或顺序不同的方法。是方法多态的体现,属于静态多态,这种多态在代码编译阶段就确定下来。
覆写:是基于继承的,要求在子类当中定义名称、返回类型、参数个数与类型与父类完全相同的方法,并且被覆写的方法不能拥有比父类更严格的访问控制权限。它是对象多态,在程序运行阶段才能体现出来。使用final修饰父类方法时,该方法不会有被覆写的机会。被覆写的方法不能为static,因为静态方法在编译时就和类的引用类型进行匹配。在子类中通过 super.方法()调用父类中被子类覆写的放法。 - 抽象类(作用之一:模板设计模式)
①抽象类和抽象方法都必须用 abstract 关键字类修饰。
②抽象类不能直接实例化,也就是不能直接用new关键字去产生对象。
③抽象类定义时抽象方法只需声明不需要实现。
④含有抽象方法的类必须被声明为抽象类,抽象类的子类必须覆写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
⑤子类使用extends 继承抽象类,一个子类只能继承一个抽象类。
⑥注意:抽象类可以有成员变量、构造方法和一般方法。
⑦抽象类不能使用final 关键字,因为使用final定义的类不能有子类,而抽象类使用的时候必须有子类,二者矛盾。
⑧抽象类可以没有抽象方法,但是没有抽象方法的抽象类也不能直接实例化。
⑨抽象类可以有 静态方法 ,跟普通类的静态方法一样。
⑩在外部抽象类上无法使用static 声明,但是内部抽象类却可以使用static 定义,使用static 定义的内部抽象类就表示一个外部类。
abstract class Book{ public abstract void printf(); static abstract class CD{ //静态内部抽象类 public abstract void get(); } } class JavaCD extends Book.CD{ public void get(){ ..... } }
- 接口:
①接口里的数据成员必须初始化,且数据成员均为常量不能更改(final 定义,final关键字可以省略)。
②接口里的方法为 abstract,并且该关键字可以省略。
③在Java8中为了避免在接口中添加新方法后要修改所有实现类,允许定义默认方法,即default 方法。在对接口进行实现时由实现类的对象调用 default 方法。
④如果一个类实现多个接口,若接口中有默认方法,不能出现同名默认方法。public interface TestInterface1 { final String name = "you"; abstract String get(); default public String getName(){ ..... } }
⑤接口作用:工厂设计模式,代理设计模式。 - 拆箱和装箱:
Java是一种面向对象编程语言,但是为了使用方便Java也提供了8种(int、char、float、double、byte、long、short、boolean)基本数据类型,虽然我们使用的时候很方便,其实是系统为我们做了 装箱和拆箱 的操作。
①装箱:就是把基本类型用它们相对应的引用类型包装起来,使它们具有对象的特质,例如把int包装成Integer,double包装成Double等等。
②拆箱:跟装箱的操作相反。 - Runtime类
Runtime表示的是运行时在每一个JVM进程之中都会存在唯一的一个Runtime 类的实例化对象,这个类很明显是单例设计模式。
取得Runtime 实例:public static Runtime getRuntime();
利用Runtime 可以取得内存的相关信息:
①最大可用内存数:public long maxMemory();
②总共的可用内存数:public long totalMemory();
③空闲内存数:public long freeMemory();
手动清除垃圾:
Runtime.getRuntime().gc(); - 两种字符串实例化方式(直接赋值方式和构造方法实例化方式)的区别
①直接赋值方式:将一个字符串的常量直接赋值给指定的字符串变量。例如:String a = "hello";
②构造方法实例化方式:通过构造方法进行赋值。例如:String a = new String("hello");
举一个实际的例子来让大家看清楚这两种赋值方式的区别:
分析直接赋值方式:在Java中,如果字符串对象使用直接赋值方式完成,第一次定义字符串的时候在堆内存空间定义一个新的字符串常量“hello”,如果后面还有其他字符串对象才用的是直接赋值的方式实例化,并且此内容已经存在,那么就不会开辟新的字符串常量,而是让其指向了已有的字符串内容,即直接赋值方式函数中的a和b指向同一块堆内存。这种设计是共享设计模式。所谓共享设计模式指的是在JVM底层准备出一个对象池(多个对象),如果现在按照某一个特定方式进行对象实例化操作,那么此对象的内容会保存到对象池中,而后如果还有其他对象也采用了固定的方式声明了与之相同的内容,则此时将不会重新保存新对象到对象池中,而是从对象池中取出已有的对象内容继续使用,这样一来可以减少垃圾空间的产生。(摘自张玉宏主编的《Java从入门到精通》一书)。public class TestString { public static void main(String[] args) { System.out.println("直接赋值方式==================="); indirectAssignment(); System.out.println("构造方法实例化方式==================="); constructorInstantiation(); } //直接赋值方式 public static void indirectAssignment(){ String a = "hello"; String b = "hello"; String c = b; System.out.println(a == b);//true System.out.println(a == c);//true System.out.println(b == c);//true a = a+" world"; System.out.println(a);//hello world System.out.println(b);//hello System.out.println(a == b);//false } //构造方法实例化 public static void constructorInstantiation(){ String a = "hello"; String b = new String("hello").intern();//手工入池 String c = "hello"; String d = new String("hello"); System.out.println(a == b);//true System.out.println(a == c);//true System.out.println(b == c);//true System.out.println(a == d);//false } }
分析构造方法实例化方式:使用构造方法实例化方式构造String 的类对象,无法进行自动入池操作,即:数据无法共享。在String 类中提供了一个方法,可以帮助用户手动入池:public String intern(); 所以出现了上面的结果。 - 堆内存和栈内存:
①堆内存:堆内存保存的是一个对象的具体信息,每一个对象保存的只是属性信息,每一块对内存的开辟都要通过关键字 new 来完成。
②栈内存:可以理解为一个整型变量,其中保存的是堆内存空间的内存地址数值(我们学过计算机组成原理的都知道内存的每一个字节都有唯一一个数值表示的地址),为了理解方便可以看做是保存的对象的名字。 - 引用传递:
除了基本数据类型的传递是值传递外,对象的传递是引用传递,也就是一块堆内存空间,同时被多个栈内存空间所指向(这里指形参和实参)。既然不同的栈内存指向了同样的堆内存空间,所以不管对哪个进行修改都会影响到原来的数据。 - Java反射机制:
是指在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。
Class 是反射操作的源头,如果要想取得Class 类的实例化对象在Java中有三种方式:
①利用Object 类的getClass() 方法,但是要求必须先产生指定类的对象才可以,几乎不用:
②利用 “类.class” 的形式取得 Class 类的对象,在Hibernate 上使用。Date date = new Date(); Class cls = date.getClass(); System.out.println(cls);
③利用Class 类提供的一个方法完成,在系统架构中使用:Class cls = Date.class; System.out.println(cls);
利用反射实例化一个Book类对象(有无参构造方法时):Class cls = Class.forName("java.util.Date"); System.out.println(cls);
利用反射实例化一个Book对象(没有无参构造方法时):Class cls = Class.forName("Book"); Book book = (Book)cls.newInstance();
public class Book { private String title; private double price; public Book(String title,double price){ this.title = title; this.price = price; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
可见如果要想实例化对象可以有两种形式:一种是通过关键字new ,另一种是通过反射机制完成。public class TestBook { public static void main(String args[]){ try { Class cls = Class.forName("Book"); Class[] clss = {String.class,double.class}; Constructor constructor = cls.getConstructor(clss); Object[] objects = {"Java",Double.valueOf(55.51)}; Book book = (Book)constructor.newInstance(objects);//利用构造函数进行对象的实例化 System.out.println(book.getTitle()); } catch (Exception e) { e.printStackTrace(); } } }
- List接口与Set接口:
①List 允许有重复元素,Set 不允许有重复元素。
②HashSet 实现了Set 接口,HashSet里面所保存的数据是不能有重复的,并且没有顺序。
③TreeSet 也实现了Set接口并且按树存储其元素,TreeSet 里面的数据会自动进行排序。
④List接口的实现类LinkedList(链接列表实现)、ArrayList(变长数组实现,属于异步处理性能比Vector高)、Vector(动态数组实现,是同步处理,性能稍低)。 - 双向迭代器(可以将列表反向输出):
public class ListIteratorTest { public static void main(String args[]){ ArrayList<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("to"); list.add("shiyan"); ListIterator<String> iterator = list.listIterator(); String temp = null; while (iterator.hasNext()) { temp = iterator.next(); iterator.set(temp+" ");//修改内容 都加一个空格 } //下面将列表中的元素反向输出 System.out.println("反向输出..."); while (iterator.hasPrevious()) {//判断是否有前一个 System.out.print(iterator.previous()); } System.out.println("原值..."); for (String tmp : list) {//通过输出我们看出 原值并没有改变 System.out.print(tmp); } } }
利用双向迭代器ListIterator的next 方法获取列表中的元素,并用set 方法修改当前元素的值。需要注意的是:因为第一个while 循环将双向迭代器iterator 移到了迭代器的尾部,所以之后才可以将列表反向输出,所以在反向输出前要保证 迭代器已经循环到尾部。另一个值得注意的是:通过查看最后一个 for 循环的输出会发现利用迭代器的set 方法所做的修改,影响到了与该迭代器相关联的ArrayList的原始值。 - 实现Runnable接口相对于继承Thread 类的优势:①避免由于Java的单继承特性带来的局限。②可以使多个线程共享相同的资源,已达到资源共享的目的。
- 用户线程与守护线程JVM 中线程分为两种:用户线程和守护线程。用户线程也称为前台线程。守护线程也称为后台线程,是守护其他线程的线程。默认创建的线程都属于普通的用户线程,只有调用了 setDaemon(true)之后(注意:此方法一定要在start方法之前调用),才能转成守护线程。
- Java 流操作:Java流操作分为两种:字节流和字符流。字符流处理的对象单元是Unicode 字符,每个Unicode 字符占据2个字节,而字节流输入输出 的数据是以单个字节(Byte)为读写单位。字符流是由Java 虚拟机将单个字节转化为两个字节的Unicode 字符,所以它对多国语言支持较好。Java的流式输入/输出建立在4个抽象类的基础上:InputStream、OutputStream、Reader和Writer。其中InputStream、OutputStream被设计成字节流类,而Reader和Writer 则被设计成字符流类。
- 对象序列化:对象序列化(也叫串行化)是指将在内存之中保存的对象转化为二进制数据流的形式的一种操作。通过将对象序列化,可以方便的实现对象的传输及保存。但是在Java 之中并不是所有对象都可以被序列化,如果一个对象需要被序列化,则此类要实现Serializable接口。这个接口属于标识接口,表示一种能力。在Java 中提供有ObjectInputStream与ObjectOutputStream 这两个类用于序列化对象的操作。①序列化操作ObjectOutputStream :
②反序列化操作ObjectInputStream:OutputStream outputFile = new FileOutputStream(new File("serialized.txt")); ObjectOutputStream oos = new ObjectOutputStream(outputFile); oos.writeObject(实现了Serializable接口的类对象); oos.close();
③transient关键字:InputStream outputFile = new FileInputStream(new File("serialized.txt")); ObjectInputStream ois = new ObjectInputStream(outputFile); //假设 Person类实现了Serializable接口 Person p = (Person)ois.readObject(); ois.close();
默认情况下,当一个类对象序列化时,会将这个类的全部属性都保存下来,如果不希望类中的某个属性被序列化可以在声明属性之前加上transient关键字。