java基础梳理

来源:互联网 发布:java获取当前编码格式 编辑:程序博客网 时间:2024/06/18 06:05
1.JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境。SDK是Software Development Kit 一般指软件开发包,可以包括函数库、编译程序等。Jdk 是java development kit,是java的开发工具包,里面包含了各种类库和工具。当然也包括了另外一个Jre.
如果安装了JDK,会发同你的电脑有两套JRE,一套位于 \jre 另外一套位于 C:\Program Files\Java\j2re1.4.1_01 目录下,后面这套比前面那套少了Server端的Java虚拟机,不过直接将前面那套的Server端Java虚拟机复制过来就行了。而且在安装JDK可以选择是否安装这个位于 C:\Program Files\Jav a 目录下的JRE。如果你只安装JRE,而不是JDK,那么只会在 C:\Program Files\Java 目录下安装唯一的一套JRE。 


  JRE是java runtime environment, 是java程序的运行环境。是面向Java程序的使用者,而不是开发者。既然是运行,当然要包含jvm,所有java类库的class文件,都在lib目录下打包成了jar。jre/bin/client里面有一个jvm.dll,那就是虚拟机。JRE的地位就象一台PC机一样,我们写好的Win32应用程序需要操作系统帮我们运行,同样的,我们编写的Java程序也必须要JRE才能运行。所以当你装完JDK后,如果分别在硬盘上的两个不同地方安装了两套JRE,那么你可以想象你的电脑有两台虚拟的Java PC机,都具有运行Java程序的功能。所以我们可以说,只要你的电脑安装了JRE,就可以正确运行Jav a应用程序。


相信大家都知道jdk的bin下有各种java程序需要用到的命令,与jre的bin目录最明显的区别就是jdk下才有javac,这一点很好理解,因为 jre只是一个运行环境而已。与开发无关,正因为如此,具备开发功能的jdk自己的jre下才会同时有client性质的jvm和server性质的 jvm, 而仅仅作为运行环境的jre下只需要client性质的jvm.dll就够了。


2.包(package):程序库单元
程序库实际是一组类文件,其中每个文件都有一个public类,因此每个文件都是一个构件,如果你希望这些构件(每一个都有它们自己分开的.java和.class文件)从属于一个群组,就可以用pachage
package mypackage;
声明该编译单元中的public类名称是位于mypackage名称的遮蔽之下的。

3.import:用来导入包,之所以要导入,就是要提供一个管理名字空间的机制。保证所有类成员的名称是彼此隔离的(不同类之间可以具有相同参数列表的方法,不同包中可以具有同名的类)。


4.安装完jdk之后,需要进行环境变量设置,一是设置JAVA_HOME环境变量,使其指向所安装JDK的根目录,另一个是将JDK的bin目录增加到Path环境变量中,这样就可以在控制台的任意目录中调用jdk所提供的命令了。


配置完成后可以通过set命令在输出中看到JAVA_HOME的定义,通过“java-version”命令可以查看java的版本。


5.Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,有Apache,Sun和其他一些公司及个人共同开发完成的。其运行时占用资源少,扩展性好,支持负载平衡与邮件服务等开发应用系统常用的功能。它是一个小型的轻量级的应用服务器,在中小型系统和并发访问用户不是很多的场合下比较适用。


6.启动Tomcat服务的时候:报Address already in use: JVM_Bind错误
原因是地址已经在使用,可能是tomcat的server.xml文件中的端口设置有问题,或者是没有关闭tomcat的情况下又启动了一次tomcat。



7.java对象初始化顺序


[1]对象在初始化过程,JVM会先去搜索该类的顶级父类,直到搜索到我们所定义的SubClass继承树上直接继承于Object类的子类,在这里就是Base类;    
[2]然后JVM会先加载Base类,然后初始化Base类的静态变量a,然后执行Base类的静态初始化块 【*:此时该类还未调用构造函数,构造函数是实例化的时候调用的】    
[3]然后JVM按照继承树往下搜索,继续加载Base类的子类,按照静态成员函数->静态成员变量->静态初始化块的顺序往下递归,直到加载完我们使用的对象所在的类。    
[4]类加载完了过后开始对类进行实例化操作,这个过程还是会先搜索到直接继承于Object类的子类,在这里就是Base类;    
[5]JVM会实例化Base类的成员函数,然后实例化成员变量,最后调用Base类的构造函数;    
[6]之后,JVM会递归往继承树下边进行调用,顺序还是遵循:成员函数->成员变量->构造函数;    
[7]最后直到SubClass类的构造函数调用完成 


8.new对象过程解析
Student st = new Student();   
上面的语句做了如下的事情:   
 ①右边的“new Student”,是以Student类为模板,在堆空间里创建一个Student类对象(也简称为Student对象)。   
 ②末尾的()意味着,在对象创建后,立即调用Student类的构造函数,对刚生成的对象进行初始化。构造函数是肯定有的。如果没创建,Java会补上一个默认的构造函数。 
 ③左边的“Student st”创建了一个Student类引用变量。  
 ④“=”操作符使对象引用指向刚创建的那个Student对象。


我们可以获得以下结论
:①一个对象引用可以指向0个或1个对象;②一个对象可以有N个引用指向它。
  

9.equals和hashcode
    (1).equals 方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等,两个对象所表达的意思是否相同。equals方法将在对象被保存到容器类中时被调用,因为集合容器不允许其中的两个相同的对象实例,其判断的依据就是通过调用该实体对象的equals方法来判断两个对象的实例是否相同。


  ==用于比较两个对象的引用是否相等和比较基本数据类型时具有不同的功能:  
 比较基本数据类型,如果两个值相同,则结果为true,而在比较引用时,如果引用指向内存中的同一对象,结果为true


 Student a=new Student();
 Student b=new Student();
 Student c=a;


 System.out.println(a.equals(b));     //false
 System.out.println(a==b);            //false
 System.out.println(a.equals(c));     //true
 System.out.println(a==c));           //true


    (2).如果想让上面的a.equals(b)为true可以对equals方法重写:原equals方法是属于Object的,重写的时候为了保证不影响到原equals方法,必须是对Object重写。重写了equals方法的同时还必须重写hashCode方法,如果不这样的话就会违反java.lang.Object的通用的hashCode的约定,从而导致该类无法和所有基于散列值的集合类(HashMap、HashSet、HashTable等)一起正常工作。


public boolean equals(Object obj){
if(this==obj) return true;
if(obj instanceof Student){
Student s=(Student) obj;
return true;
}
return super.equals(obj);
}
System.out.println(a.equals(b));    //true
System.out.println(a==b);           //false


10.java中static修饰符的代码的加载顺序 
    (1):首先加载被final static 修饰的原始数据类型的数据成员(若是其他类型的对象,包括原始数据类型的类包装器,如Integer,也排在第3加载)。


    (2):其次加载处于static块中的代码块。


    (3):最后才是加载只被static修饰的数据成员。


测试实例:


public class TestStatic {
public static int a = 10;

public static final String s1 = new String("sss");

public static final String s2 = "aaaaaaa";

public static final int b = new Integer(20);

public static final int c = new Random().nextInt();

public static final int d = 250;

static {
a = 0;
System.out.println("initable InitSquence");
}

public static void main(String[] args) {

System.out.println(TestStatic.s1);
}

}


输出:->initable InitSquence

      ->sss
 

如果改成:System.out.println(TestStatic.s2);


则输出:->initable InitSquence
 
 ->aaaaaaa


如果改成:System.out.println(TestStatic.b);

则输出:->initable InitSquence


        ->20


如果改成:System.out.println(TestStatic.a);

则输出:->initable InitSquence

        ->0


如果改成:System.out.println(TestStatic.d);


则输出:->initable InitSquence

 ->250


11.Java中abstract和interface的区别 


abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。 abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于 abstract class和interface的选择显得比较随意。 其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一一番剖析,试图给开发者提供一个在二者之间进行选择的依据。
 
    一、理解抽象类 abstract class和interface在Java语言中都是用来进行抽象类(本文中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而abstract class为Java语言中用于定义抽象类的一种方法,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带来什么好处呢? 在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。 并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如:如果我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一组固定行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而具体实现则表现为派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。 
    
    二、从语法定义层面看abstract class和interface。在语法层面,Java语言对于abstract class和interface给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。


使用abstract class的方式定义Demo抽象类的方式如下:
abstract class Demo { 
  abstract void method1(); 
  abstract void method2(); 
  … 
} 


使用interface的方式定义Demo抽象类的方式如下: 
interface Demo { 
  void method1(); 
  void method2();
  … 



    在abstract class方式中,Demo可以有abstruct数据成员,也可以有非abstarct的成员方法,而在interface方式的实现中,Demo只能够有static final的数据成员,但在interface中一般不定义数据成员,而且所有的成员方法都是abstract的。


    从某种意义上说,interface是一种特殊形式的abstract class。从编程的角度来看,abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。 
    首先,abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系(单继承)。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。 
    
    其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会增加一些复杂性,有时还会造成很大的麻烦。在抽象类中不能定义默认行为还存在另一个比较严重的问题,那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面(一般通过abstract class或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果界面是通过abstract class来实现的,那么可能就只需要修改定义在abstract class中的默认行为就可以了。 同样,如果不能在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了 "one rule,one place"原则,造成代码重复,同样不利于以后的维护。因此,在abstract class和interface间进行选择时要非常的小心。 


    三、从设计理念层面看abstract class和interface 
    上面主要从语法定义和编程的角度论述了abstract class和interface的区别,这些层面的区别是比较低层次的、非本质的。本文将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。前面已经提到过,abstarct class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的。对于interface来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。 
    考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示: 使用abstract class方式定义Door: 
    abstract class Door { 
      abstract void open(); 
      abstract void close(); 
    } 
    使用interface方式定义Door: 
    interface Door { 
      void open(); 
      void close(); 
    } 


    其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。 


解决方案一: 简单的在Door的定义中增加一个alarm方法,如下: 


abstract class Door { 
  abstract void open(); 
  abstract void close(); 
  abstract void alarm(); 



或者 


interface Door { 
  void open(); 
  void close(); 
  void alarm(); 

    
那么具有报警功能的AlarmDoor的定义方式如下: 
class AlarmDoor extends Door {
  void open() { … } 
  void close() { … } 
  void alarm() { … } 



或者
 
class AlarmDoor implements Door { 
  void open() { … } 
  void close() { … } 
  void alarm() { … } 
} 


    这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。 


解决方案二: 既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用 abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。 显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。 如果两个概念都使用interface方式来定义,那么就反映出两个问题: 1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器? 2、 如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。 
    如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定 义。如下所示: 
abstract class Door { 
  abstract void open(); 
  abstract void close(); 



interface Alarm { 
  void alarm(); 



class AlarmDoor extends Door implements Alarm { 
  void open() { … } 
  void close() { … } 
  void alarm() { … } 



    这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系,interface表示的是"like a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有 Door的功能,那么上述的定义方式就要反过来了。 


    abstract class和interface是Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系(虽然都能够实现需求的功能)。这其实也是语言的一种的惯用法,希望 读者朋友能够细细体会。











原创粉丝点击