Java编程思想(一):大杂烩

时间:2023-03-09 04:41:41
Java编程思想(一):大杂烩

在java中一切都被视为对象。尽管一切都是对象,但是操纵的标识符实际上是对象的一个引用,可以将引用想象成是遥控器(引用)来操纵电视机(对象)。没有电视机,遥控器也可以单独存在,即引用可以独立存在,并不需要有一个对象与之关联,如下:
   String s;
 上面只是创建s这个引用,并不是对象。

*基本类型
      一般类型可以通过new将对象存储在"堆"里,但是用new创建一个对象-特别是小的,简单的变量,往往不是有效的。故Java采与C和C++一样的方法,不用new来创建这些变量,而是创建一个并非引用的自动变量。这个变量直接存储值并置于堆栈中,因此更加高效。
      基本类型具有的包装类,使得可以在堆中创建一个非基本对象。
 *Java数组
     当创建一个数组对象时,实际就是创建了一个引用数组,并且每个引用都会自动被初始化为一个特定的值,该值拥有自己的关键字null,一旦Java看到null,就知道这个null还没有指向某个对象。在使用任何引用前,必须为其指定一个对象。
     还可以创建用来存放基本数据类型的数组,同样编译器也能确保这种数组的初始化,把这种数组所占的内存全部置为0.
 *作用域
     作用域决定了在其内定义的变量名的可见性和生命周期。在作用域内定义的变量只可用于作用域结束之前。
     {
        String s=new String("abs");
     }
     引用s在作用域终点就消失了,然而,s指向的String对象仍继续占据内存空间。
*字段和方法
    字段可以是任何类型的对象,可以通过其引用与其进行通信,也可以是基本类型中的一种。如果字段是某个对象的引用,那么必须初始化该引用,以便其与一个实际的对象相关联。
    若某个类的某个成员是基本数据类型,即使没有进行初始化,Java也会保证它会获得一个默认值。
    如果字段是非基本类型的,则不会有默认值(也可以说有默认值,即null,如果调用,就会出现异常),使用之前必须进行初始化。
    在Java调用方法,传递给方法的参数都是对象的引用(也就是值传递)。

*static关键字
    执行new创建对象时,数据空间才被分配,方法才能被外界调用。
    当声明一个事物为static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。
*赋值
    复制的操作符"=",意思是取右边的值复制给左边。右值可以是任何常数,变量或表达式,但左值必须是一个已命名的变量。
    对于基本类型的赋值时很简单的,基本类型存储了实际的数值,而并非指向了一个对象的引用,所以为其赋值的时候,是直接将一个地方的内容复制到另一个地方。例如对基本数据类型使用a=b,那么b的内容就复制给a,若接着又修改了a,而b根本不会受这种修改的影响。
    但是为对象"赋值"的时候,就不同了。对象赋值的时候,操作的是引用,倘若"将一个对象赋值给另一个对象",实际上是将"引用"从一个地方复制到另一个地方,这意味着假若对象使用c=d,那么c和d都指向原本只有d指向的那个对象。
*算术操作符
    Java的基本算术操作符与其他大多数程序设计语言是相同的。整数除法会直接去掉结果的小数位,而不是四舍五入。
    "=="和"!="比较的是对象的引用,尽管a和b对象的内容一样,但是a==b仍为false;如果想比较两个对象的内容,重写对象的equals()方法,对象默认的equals()比较的是引用;但是这个方法不适合基本类型,基本类型只需要使用"=="和"!="就行了。
*类型转换
    在适当的时候,Java会将一种数据类型自动转换为另一种。
    Java允许把任何基本数据类型转换成别的基本类型,但是布尔类型除外,后者根本不允许进行任何类型的转换处理。
    float或double转型为整型数值的时候,总是对数字执行截尾。
    在char,byte,short中,对于这几个类型进行算术运算的时候,都会得到一个int结果,必须显式地将类型转换回原来的类型。
*return
    return有两方面的作用:第一,指定一个方法返回什么值;另一个方面,它会导致当前方法的退出,并返回那个值。
    如果在返回void的方法中没有return语句,那么在该方法的结尾处会有一个隐式的return。
*break&&continue
    break用于强制退出循环(当前循环),不执行循环中剩余的语句。而continue则停止当前的迭代,然后退回循环的开始处,开始下一次迭代。

*构造函数
    不接受任何参数的构造器叫做默认的构造器。
    在一个构造函数中应用另一个构造函数的时候,可以使用this(参数列表)的方式。但是一个构造函数中最多只能调用一个其他的构造函数。
    在类里定义一个对象的引用时,如果不将其初始化,此引用就会获得一个特殊值null。
    public class Counter{
        int i;
 Counter(){ i=7;}
    }
    上述代码中,i首先会被置为0,然后变为7.对于所有基本类型和对象引用,包括在定义时已经指定初值的变量,这种情况都是成立的。
    初始化顺序:
         *在类的内部,变量定义的先后顺序决定了初始化的顺序。即变量定义散步于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
  *无论创建多少个对象,静态数据都只占用一份存储区域。如果一个static修饰的是基本类型的成员变量,那么它就会获得基本类型的标准初值,如果它是一个对象的引用,那么它的默认值就是null;
总结一下对象的创建过程,假设有个名为Dog的对象:
    1.即使没有使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看作是静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
    2.然后载入Dog.class,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。
    3.当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
    4.这块存储空间会被清0,这就是自动地将Dog对象中所有基本数据类型都设置成了默认值,而引用都设置成了null。
    5.执行所有出现于字段定义出的初始化动作。
    6.执行构造器。
*static块&&非static块
    static int i;
    static{
       i=47;   
    }
    上述的代码也是只执行一次,当首次生成这个类的对象时,或者首次访问属于那个类的静态数据成员时。
    非static代码块,用来初始化每一个对象的非static变量,每次生成该类的对象的时候,都会执行代码块中的代码,先于构造函数执行。
*枚举
     创建枚举类型,如下:
        public enum Spiciness{
    NOT,MILD,MEDIUM,HOT,FLAMING
 }
     使用enum,需要创建一个该类型的引用,并将其赋值给某个实例:
        public class SimpleEnumUse{
    public static void main(String[] args){
       Spiciness howHot=Spiciness.MEDIUM;
       System.out.println(hoeHot);   //output:MEDIUM
    }
 }
   在创建enum时,编译器会自动加一些有用的特性,例如:它会创建toString()方法,以便能方便现实某个enum实例的名字。同时编译器还会创建ordinal()方法,用来表示某个特定enum常量的声明顺序,以及static values()方法,用来按照enum常量的声明顺序,产生由这些常量值构成的数组。如下:
     public class EnumOrder{
         public static void main(String[] args){
      for(Spiciness s: Spiciness.values())
          System.out.println(s+".ordinal "+s.ordinal());
  }
     }
 *Java访问权限修饰符
    无论是字段还是方法,如果不提供任何的权限修饰符,则意味着它是包访问权限的。这就意味着当前包里的所有其他类对那个成员都有访问权限,但是对于这个包之外的所有类,这个成员确实private。由于一个编译单元(即一个文件),只能隶属于一个包,所以经由包访问权限,处于同一个编译单元中的所有类彼此之间都是自动访问的。
    关键字private的意思是,除了包含该成员的类之外,其他任何类都无法访问这个成员

protected关键字提供包访问权限,同时,子类也可以访问protected修饰的字段和方法。
    private和protected都不可以修饰class,所以类的权限只有包访问权限和public。

*final关键字
     必须在域的定义处或者每个构造器中使用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因所在。
     Java允许在参数列表中以声明的方式将参数指明为final,这意味着你无法在方法中更改参数引用所指向的对象。
     final方法:
         使用final方法的原因有两个:第一是把方法锁定,以防止继承类修改它的含义,想确保在继承中使方法行为保持不变,并且不会被覆盖;第二原因是效率。如果一个方法指明为final,就是同意编译器针对该方法的所有调用都转为内嵌调用,去除调用方法的开销。(不过现在虚拟机已经有了大大的改进,已经不需要使用final方法来进行优化了)
     final类:
          当将某个类定义为final的时候,就表明了你不打算继承该类,而且也不v 1允许别人这么做,换句话说,处于某种考虑,你对该类的设计永远不需要任何改动,或者处于安全考虑,不希望它有子类。
*初始化与类的加载
     一般来说,Java类的代码在初次使用的时候才加载,比如,创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载。所有的static对象和static代码段都会在加载时依程序中的顺序而一次初始化。
   多态的缺陷:
   1."覆盖"私有方法
      public class PrivateOverride{
          private void f(){ print("private f()");}
   public static void main(String[] args){
      PrivateOverrride po=new Derived();
      po.f();
   }
      }
      class Derived extends PrivateOverrride{
          public void f(){ print("public f()");}
      }
     //output如下:
     private f()
     出现上面的情况是由于private方法被自动认为是final方法,而且对导出类是屏蔽的。因此,Deriver类中的f()方法就是一个全新的方法;既然基类中的f()方法在类Derived中是不可见的,因此甚至不会被重载。
     结论:只有非private方法才可以被覆盖。
   2.域与静态方法
      在基类与子类中定义一个名字相同的字段,容易让人混淆,但是开发中应尽量将字段都设置为private。
      如果某个方法是静态的,那么它的行为就不具有多态。
*构造器
    构造器实际上是static方法,只不过该static声明是隐式的。
    在子类的构造主体中,如果没有明确指定调用某个基类构造器,他就会默认地调用默认构造器。如果不存在默认的构造器就会编译报错。

*接口
   包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须限定为抽象类,否则编译报错。
   可以在关键字interface前面加public,如果不加public,那么接口默认具有包访问权限,接口也可以包含字段,但是这些字段隐式地是static和final的,而且默认是public的。
   可以在接口中显式地将方法声明为public,即使不这么做,他们也是public的。

*容器迭代器
    迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。
    Iterator-->只能向后移动。
    ListIterator-->是Iterator的子类型,只能用于List类的访问,并且可以双向移动。

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(如插入和删除)时比ArrayList高效,但在随机访问操作方面却要逊色一些。
*Java异常处理
    发现错误的理想时机是在编译阶段,也就是在你试图运行程序之前,然而,编译期间并不能找出所有的错误,余下的问题必须在运行期间解决。
    异常处理是Java中唯一正式的错误报告机制,并且通过编译器强制执行。
    异常情形(exceptional condition)是指阻止当前方法或作用域继续执行的问题,把异常情形与普通问题相区分很重要,所谓的普通问题,是指在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题,所能作的就是从当前环境跳出,并且把问题交给上一级环境,这就是抛出异常时所发生的事情。
    当抛出异常的时候,首先,同Java中其他对象的创建一样,将使用new在堆上创建异常对象,然后,当前的执行路径被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序,这个恰当的地方就是异常处理程序,它的任务就是ijiang程序从错误状态中霍夫,以使程序能要么换一种方式运行,要么继续运行下去。
    所有的标准异常类都有两个构造器:一个是默认构造器,另一个是接受字符串作为参数,以便把相关信息放入异常对象的构造器。
    创建自定义的异常类型,必须从已有的异常类继承,最好是选择意思相近的异常类继承。

属于运行时异常会被虚拟机抛出,所以不必在异常说明中巴他们列出来,这些异常都是从RuntimeException类继承而来,因此运行时异常也被称为"不受检查的异常"。这种异常属于错误,将被自动捕获。RuntimeException代表着编程错误。
    Java标准中还自带一些异常,如NullPointerException异常。
*finally
   异常无论是否被抛出,finally子句总是能被执行。 
   抛出异常的时候,异常处理系统会按照代码的书写顺序找出"最近"的处理程序,找到匹配程序之后,它就认为异常得到处理,然后就不再继续寻找。
*字符串
   String对象是不可变的,查看JDK文档就会发现,String类中每一个看起来会修改String值得方法,实际上就是创建一个全新的String对象,以包含修改后的字符串内容,而最初的String对象丝毫未动。
   Java不允许程序员重载任何操作符,String的"+""+="是Java中仅有的两个重载过的操作符。
   Formatter类
   在Java中所有新的格式化功能都是由java.util.Formatter类来处理,当创建一个Formatter对象的时候,需要向其构造器中传递一些信息,告诉它最终的结果将向哪里输出:
   public class Turtle
{
    private String name;
    private Formatter f;
    public Turtle(String name,Formatter f){
        this.name=name;
        this.f=f;
    }
    public void move(int x,int y){
        f.format("%s The Turtle is at (%d,%d)\n",name,x,y);
    }
    public static void main(String[] args){
        PrintStream outAlias=System.out;
        Turtle tommy=new Turtle("tommy",new Formatter(System.out));
        Turtle terry=new Turtle("terry",new Formatter(outAlias));
        tommy.move(0,0);
        terry.move(4,8);
    }
}
此外,String.format();是一个static方法,它接受与Formatter.format()一样的参数,但返回一个String对象。
*Scanner类
     Scanner的构造器能够接受任何类型的输入对象,包括File,InputStream,String等等。
*StringTokenizer类
     已经废弃不用了,用来分词。

*class
一旦某个类的Class对象载入内存,它就被用来创建这个类的所有对象。
Class引用总是指向某个Class对象,它可以制造类的实例,并包含作用于这些实例的所有方法代码,它还包含该类的静态成员。