黑马程序员:类和对象

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、类的声明
格式(最小描述):

class MyClass{
        // field, constructor, and
        // method declarations
}

constructors用于初始化新的对象

field用于描述类和对象的状态(state)
method用于实现类和对象的行为(behavior)
一般类声明按顺序包括以下几部分
1. 访问权限修饰词,如 public、private等。
2. 按规范,类名的首字母要大写
3. 如果有父类则在类名后加“ extends 父类名”,最多只能有一个父类。
4. 如果有实现的接口在后面加“ implements 接口名1,接口名2...”, 一个类可实现多个接口。
5. 类体,花括号{}括起来的内容
注:“extends” 和 “implements”使用第三人称单数形式,加‘s’。
例:
public class MyClass extends MySuperClass implements YourInterface {
    // field, constructor, and
    // method declarations
}

二、成员变量声明
这有几种变量:
• 类中的成员变量,称为字段(fields)
• 方法或代码块中的变量,称为局部变量(local variables)
• 方法声明中的变量,称为参数(parameters)
声明成员变量一般有三部分:
1. 零个或以上访问修饰词,如public 或 private;
2. 成员变量的类型,如int, float, 类名;
3. 成员变量名

1) 访问修饰词
从左数,第一个修饰词让你控制其他类对该类成员的访问。
• public —— 可以让所有类访问
• private —— 只可以在该类内部访问
为了封装,一般让成员变量是private的。外部其他类只能通过该类public的方法间接访问成员变量。
public class Bicycle {
    private int cadence;
    private int gear;
    private int speed;
    
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }
    
    public int getCadence() {
        return cadence;
    }
    
    public void setCadence(int newValue) {
        cadence = newValue;
    }
    
    public int getGear() {
        return gear;
    }
    
    public void setGear(int newValue) {
        gear = newValue;
    }
    
    public int getSpeed() {
        return speed;
    }
    
    public void applyBrake(int decrement) {
        speed -= decrement;
    }
    
    public void speedUp(int increment) {
        speed += incremennt;
    }
}


2) 类型
所有变量必须属于各自的类型,可以使用基本类型,如int, float, boolean等等。或者使用引用类型,如字符串,数组和类。

3) 变量名
所有变量(成员变量、局部变量或参数)适用同样的命名规则和规范:
• 变量名是大小写敏感的。一个变量名可以是任意合法的标识—— 无限长的以字母,“$”,和“_”开头的Unicode字母和数字的序列。规范中,变量是以字母开头,“$”不应该用于变量名。以“_”开始的变量名是不被提倡的。
• 随后的字符可以是字母,数字,“$”,“_”。规范中变量名应该使用完整的单词,而避免使用含义模糊的缩写。这可以提高代码的可读性。另外,变量名不能是保留字,如int, class, extends等。
• 如果变量名只有一个单词,字母全部小写;如果包含多个单词,第一个单词之后的单词全部首字母大写,如gearRatio。如果你的变量存储一个常量如static final int NUM_GEARS = 6,则应该大写每个字母,单词之间用“_”分割。一般“_”不用于其它地方。
• 类名的首字母大写
• 方法名的第一个单词应该是动词性的

三、方法声明
一个典型的方法声明:
public double calculateAnswer(double wingSpan, int numberOfEngines,
double length, double grossTons){
    // do the calculation here
}

一个方法声名只需要包含返回类型,名字,“()”,“{}”和其中的函数体。
一般,方法声明包含6 部分:
1. 修饰词,如public, private等
2. 返回类型,方法返回值的类型或者“void”无返回类型
3. 方法名,遵从变量名的规范,但有一点不同,方法名的第一个单词应该是动词性的
4. “()”中的参数列表——用逗号分隔的传入参数列表,格式:参数类型1 参数名1,参数类型2 参数名2...
5. 异常列表
6. “{}”中的方法体。 方法体中包含局部变量声明。
方法签名:就是方法名和参数类型,如calculateAnswer(double, int, double,double)

方法重载
Java支持方法重载,Java可以通过不同的参数列表区分方法签名。
重载的方法必须有不同数量或者类型的参数。
注意:重载方法要慎用,它可能使代码缺少可读性。

四、构造函数

一个类包含构造函数,用于根据类创建对象。
构造函数和方法声明相近,但是没有返回类型。如:
public Bicycle(int startCadence, int startSpeed, int startGear) {
    gear = starGear;
    cadence = startCadence;
    speed = startSpeed;
}

创建一个新的叫myBike的Bicycle对象,构造函数被new操作调用:
Bicycle myBike = new Bicycle(30, 0, 8);
new Bicycle(30, 0, 8)在内存中为对象开辟一个空间,并初始化字段(field);
一个类可以有多个构造函数,它们之间是函数重载的关系。如果存在两个及以上构造函数的方法签名相同,会引起编译时错误。
你可以不提供任何构造函数,此时编译器会提供一个无参的构造函数。这个无参构造函数会调用父类的无参构造函数,如果父类没有无参的构造函数,编译器报告,所以应该验证这一点。如果没有明确指定的父类,那么它会有一个隐含的父类Object,Object中有无参的构造函数。
你可以通过super()调用父类的构造函数。
构造函数前可以有访问修饰词,如果其它的类不能调用某个类的构造函数,那么其它类就不能直接创建这个类的对象,但是可以使用已经创建的该类对象的引用。
如果构造函数被声明为private,那么该类可以通过public方法创建自己对象并返回该对象。
五、向函数传信息
函数的参数类型可以是任何类型。包括基本数据类型,如double类型,int类型以及引用数据类型如对象和数组。
函数的信息传入都是直接传值的,如:

static int getMax(int first,int second) {
    if(first > second) {       
        return first;         // first is the bigger one
    }
    else{
        return second;    // second is the bigger one , or first is equal to second
    }
}
public static void main(String[] args) {
    int firstNumber = 6;
    int secondNumber = 4;
    int maxNumber = getMax(firstNumber,secondNumber);  // store the returned value in maxNumber
      
}


getMax(firstNumber,secondNumber) 中,将firstNumber的值(integer)赋给getMax的第一个参数first,将secondNumber的值(integer)赋给getMax的第二个参数second。参数的作用域在函数内,函数返回后,参数就不存在了。

static void sortInteger(int[] intArray) {   // sort ascending
    int tempInt;
    for(int i = 0; i < intArray.length - 1; i++) {
        for(int j = i + 1; j < intArray.length; j++) {
            if(intArray[i] > intArray[j]) {
                tempInt = intArray[i] ;
                intArray[i] = intArray[j];
                intArray[j] = tempInt;
            }
        }
    }           
}
public static void main(String[] args) {
    int[] myArray = {1,5,3,7,4};
    sortInteger(myArray);
}


main中sortInteger(myArray)在执行函数体内代码前,做了一件事:intArray = myArray。因此,是将myArray中的值赋给intArray,而myArray中是数组{1,5,3,7,4}的引用地址。所以,在sortInteger内对intArray的操作实际就是对myArray的操作,函数执行完成后,myArray变成{1,3,4,5,7}。
对象也是如此,传递的是对象的地址值,因此,Java并不会为一个对象或数组创建副本。
对象
一、创建对象
1)声明指向对象的引用类型变量
格式: type name; // 这告诉编译器你使用name 指向那些类型是type的数据,对于基本数据类型,这个声明也为变量保留合适的内存空间。
如果你声明一个引用类型的变量,而且没有在声明时赋值,那么它没有值,直到创建一个对象并赋值给它。也就是说,只是声明一个引用型变量不会创建对象。为了创建对象,需要使用new操作,在使用引用类型变量之前,必须赋给它一个对象。
2) 实例化类
new操作通过为新对象分配内存并返回指向该内存的一个引用来实例化一个类,new操作也调用了对象的构造函数。
new操作需要跟一个构造函数的调用,构造函数的名称指明了类的名称。
new操作返回的引用可以直接在表达式中使用,用于访问对象的成员。
3) 初始化一个对象
public class Point {
    public int x = 0;
    public int y = 0;
    public Point(int a,int b) {   // constructor
        x = a;
        y = b;
    }
}

下面的描述传递给构造函数Point 23和94作为参数:

Point originOne = new Point(23, 94);

结果,创建了一个新对象,对象的成员变量x、y分别赋值为a和b,并将引用传给originOne。
二、使用对象
1) 访问一个对象的成员变量
类内的代码可以通过成员变量的名字访问它们。类外的代码必须在对象引用或其表达式之后加“.”,再加成员变量名来访问成员变量。如:myCircle.radius。一般成员变量是设为私有的,而对外提供public方法访问。如:myCircle.getRadius()。
2) 调用对象的方法
类内的代码可以通过成员函数的名字加参数调用它们。类外的代码必须在对象引用或其表达式之后加“.”,再加函数名来调用它们。如:myCircle.draw(), 要求调用对象myCircle中的draw()函数。
3) 垃圾收集器
Java 平台允许你创建任意多的对象(只受系统限制),并且你无需考虑释放它们。Java runtime environment会在一个对象长期不使用(没有引用变量指向它)后,删除它。这叫垃圾回收机制,但它只是在合适的时候清理内存中的垃圾。
更多
一、函数返回值
一个函数会在以下情况中,返回调用它的函数:
• 完成函数内所有的语句。
• 遇到return语句。
• 抛出异常时。
所以可以使用“return 变量或数据”,返回函数运行得到的结果,其中变量和数据必须是函数声明中的返回值类型。
如果函数返回类型是void,说明不能在函数中使用return语句,只有等到函数运行结束或抛出异常后返回。
如果函数的返回类型是类名,那么return后可以是该类或其子类的对象引用。
注意:函数的返回类型也可以是接口名,这时return语句后的对象引用应该是实现了该接口的类的对象引用。
二、使用this关键字
在一个类的成员方法或构造函数中,局部的变量名可能和成员变量同名,这时不能简单通过名称来访问成员变量,而需要用“this.成员变量名”来访问成员变量。
this代表的是本类,调用某个成员函数时,比如myCircle.draw(2.5)(2.5是半径)除了将半径传给函数draw的参数radius外,还将myCircle的引用传递给myCircle中的this,所以this就表示myCircle本身。
类中的构造函数可以使用this调用其他的构造函数,如this(),this(2.5),但是到目前为止,调用构造函数的语句只能放在构造函数体内的第一行。
三、控制类成员的访问权限
• 在顶层(类)上可以使用public或默认访问修饰词
• 在成员层(成员声明)上可以使用public、private、protected和默认访问修饰词
一个类被声明为public时,该类可以被所有类访问;如果没有修饰词,说明该类只可以被同包的类访问。
一个类成员如果声明为public的,那么该成员可以被能够访问该类的所有类访问;如果没有修饰词,该成员只能被能够访问该类的所有类中的同包类访问;如果类成员声明为protect,该成员除了可以能够被访问该类的所有类中的同包类访问,还可以被包外的子类访问;如果类成员声明为private,则它只能被类中的成员访问。
访问控制
ModifierClassPackageSubclassWorld
publicYYYY
protectedYYYN
no modifierYYNN
privateYNNN


选择访问修饰词的几点建议:
• 用最严格的访问控制修饰词标记独有的成员。使用private,除非有一个好的理由不这么做。
• 避免public修饰成员变量,除非是常量。 public成员易于访问但会限制修改代码的灵活性。
四、类成员
使用static关键字创建的成员变量和成员方法属于类而非某个类的实例。
1) 类变量
当大量同一个类的对象被创建,它们每一个都有它们各自的实例变量。
当你想类的所有对象都共享同一份数据时,可以使用static关键字修饰相应的变量。在类的外部,你可以使用类名直接访问这些变量。当然也可以使用对象引用访问这些变量,但是这不被提倡,因为它不能清楚的说明这些变量是类变量。
2) 类方法
那些使用static修饰的方法可以通过类名调用而不用先创建类的对象。
同样你也可以使用对象引用来调用这些方法,基于同样的原因,这也是不被提倡的。
通常,使用static修饰的方法来访问static修饰的成员变量。
关于static和非static的变量方法访问的规则:
• 实例方法可以直接访问实例变量和实例方法
• 实例方法可以直接访问类变量和类方法
• 类方法可以直接访问类变量和类方法
• 类方法不可以直接访问实例变量和实例方法——必须使用对象引用,而且类方法不能使用this关键字,因为这里this没有指向任何对象。
3)常量
static关键字可以和final关键字合用,也用于定义常量。final修饰词说明变量的值不可以改变。
注意:如果一个基本类型或字符串类型定义为常量而且其值在编译时已确定,编译器将会使用常量的值替换程序中所有该常量名。这叫做编译时常量。如果常量值由外界修改,你需要重新编译所有使用该常量的类,以保证它们得到当前值。

五、初始化字段
成员变量声明时对其进行初始化赋值,但是这种格式的初始化因为太简单而有限制,如果初始化需要逻辑操作,简单的赋值就不适合了。实例变量可以在构造函数中赋值,并且可以使用逻辑操作和错误处理。为了让类变量具有同样的能力,Java包括static初始化块。
注意:尽管把字段声明放在类定义的开始部分是普遍的做法,但是这不是必须的。只要这些字段在被使用前声明和初始化就可以。
1) 静态初始化块
静态初始化块是由一个普通的“{}”中的代码块,并用static关键字作为先导。如:
static { 
        // whatever code is needed for intialization goes here
}

一个类可以有任意数量的静态初始化块,它们可以出现在类中的任意地方。
另一个选择是使用一个私有的静态方法完成初始化,如:

class Whatever {
    public static varType myVar = initializeClassVariable();
    
    private static varType initializeClassVariable() {
        // initialization code goes here
    }
}

使用私有静态方法的优点是它们可以在你需要时重新初始化类变量。

2) 初始化实例成员
正常情况下,你可以将实例变量的初始化代码放在构造函数中。你还可以使用初始化块和final方法完成这个工作。初始化块和静态初始化块很像,只是没有static关键字:
{
    // whatever code is needed for initialzation goes here
}

Java编译器将初始化块拷到每一个构造函数中。因此,这个方法用于在多个构造函数中共享代码。
一个final方法不能被子类重载,这一方法在子类想重用初始化方法时十分有用。如:

class Whatever {
    private varType myVar = initializeInstanceVariable();
    
    protected final varType initializeInstanceVariable() {
        // initialization code goes here
    }
}


这个方法是final的,是因为在实例初始化时调用non-final方法可能引发问题。


嵌套类
Java允许你在类的内部定义另外一个类,定义的类称为嵌套类。
嵌套类包括两种:static和non-static的。声明为static的称为静态嵌套类,non-static的称为内部类。如:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}


内部类可以访问外部类的其他成员,即使是私有成员。静态嵌套类则不能访问外部类的其他成员。作为外部类的成员,嵌套类可以声明为private, public, protect, 或包私有。
为什么使用嵌套类?
• 它是一种的合乎逻辑的组合类的方式,它将只使用在那个地方的类组合在一起。如果一个类只对其它一个类有用,那么把它嵌入到那个类中,保持它们在一起是合理的。嵌入这样的“辅助类”使它们的包更简单。
• 它增强了封装(encapsulation):两个顶层的类,A和B,B如何访问A中private的成员。将B放到A内就可以这样。
• 它可以使代码更易读、更易维护:在顶层类中嵌入小的类可以将代码放到离使用它更近的地方。
静态嵌套类
像类方法和类变量一样,静态嵌套类与它的外部类相关联。同样,它也不能直接访问定义在外围类中的实例变量和实例方法,而只能使用对象引用来访问它们。
注意:一个静态类访问外部类的实例成员,与其它类访问它的外部类的实例成员的方式和权限是相同的。也就是说,为了封装方便,一个静态嵌套类就像一个顶层类嵌套在另一个顶层类一样。
访问静态嵌套类使用外部类的名字:OuterClass.StaticNestedClass
创建静态嵌套类的对象:OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
内部类
正如实例方法和实例变量一样,一个内部类与它的外部类相关联,而且它可以直接访问对象的方法和字段。因为内部类与实例关联,它自己不能定义任何static成员。
一个内部类实例只能在外部类的实例中存在,并且可以直接访问外部实例的方法和字段。
要实例化一个内部类,你必须首先实例化外部类。接着创建内部类对象:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
还有两种特殊的内部类:局部类和匿名类。

Serialization
Serialization of inner classes, including local and anonymous classes, is strongly discouraged. When the Java compiler compiles certain constructs, such as inner classes, it creates synthetic constructs; these are classes, methods, fields, and other constructs that do not have a corresponding construct in the source code. Synthetic constructs enable Java compilers to implement new Java language features without changes to the JVM. However, synthetic constructs can vary among different Java compiler implementations, which means that .class files can vary among different implementations as well. Consequently, you may have compatibility issues if you serialize an inner class and then deserialize it with a different JRE implementation. See the section Implicit and Synthetic Parameters in the section Obtaining Names of Method Parameters for more information about the synthetic constructs generated when an inner class is compiled.

一、内部类
Inner Class Example
To see an inner class in use, first consider an array. In the following example, you create an array, fill it with integer values, and then output only values of even indices of the array in ascending order.
The DataStructure.java example that follows consists of:
•The DataStructure outer class, which includes a constructor to create an instance of DataStructure containing an array filled with consecutive integer values (0, 1, 2, 3, and so on) and a method that prints elements of the array that have an even index value.
•The EvenIterator inner class, which implements the DataStructureIterator interface, which extends the Iterator< Integer> interface. Iterators are used to step through a data structure and typically have methods to test for the last element, retrieve the current element, and move to the next element.
• A main method that instantiates a DataStructure object (ds), then invokes the printEven method to print elements of the array arrayOfInts that have an even index value.

public class DataStructure {
    
    // Create an array
    private final static int SIZE = 15;
    private int[] arrayOfInts = new int[SIZE];
    
    public DataStructure() {
        // fill the array with ascending integer values
        for (int i = 0; i < SIZE; i++) {
            arrayOfInts[i] = i;
        }
    }
    
    public void printEven() {
        
        // Print out values of even indices of the array
        DataStructureIterator iterator = this.new EvenIterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println();
    }
    
    interface DataStructureIterator extends java.util.Iterator<Integer> { } 

    // Inner class implements the DataStructureIterator interface,
    // which extends the Iterator<Integer> interface
    
    private class EvenIterator implements DataStructureIterator {
        
        // Start stepping through the array from the beginning
        private int nextIndex = 0;
        
        public boolean hasNext() {
            
            // Check if the current element is the last in the array
            return (nextIndex <= SIZE - 1);
        }        
        
        public Integer next() {
            
            // Record a value of an even index of the array
            Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
            
            // Get the next even element
            nextIndex += 2;
            return retValue;
        }
    }
    
    public static void main(String s[]) {
        
        // Fill the array with integer values and print out only
        // values of even indices
        DataStructure ds = new DataStructure();
        ds.printEven();
    }
}  


The output is:
0 2 4 6 8 10 12 14
Note that the EvenIterator class refers directly to the arrayOfInts instance variable of the DataStructure object.
You can use inner classes to implement helper classes such as the one shown in the this example. To handle user interface events, you must know how to use inner classes, because the event-handling mechanism makes extensive use of them.

二、本地类(局部类)
定义在一个语句块中的类称为局部类。一般局部类定义在函数体内。
局部类的声明
可以在任何语句块中定义局部类。如:

public class LocalClassExample {

    static String reqularExpression = "^[0-9]";
    
    public static void validatePhoneNumber {
        String phoneNumber1, String phoneNumber2) {
        
            final int numberLength = 10;
            
            //Valid in JDK 8 and later;
            
            // int numberLength = 10;
            
            class PhoneNumber {
                String formatedPhoneNumber = null;
                
                PhoneNumber(String phoneNumber) {
                    // numberLength = 7;
                    String currentNumber = phoneNumber.replaceAll ( regularExpression, "" );
                    if (currentNumber.length() == numberLength)
                        formattedPhoneNumber = currentNumber;
                    else
                        formattedPhoneNumber = null;
                    }
                }
                
                public String getNumber() {
                    retrun formattedPhoneNumber;
                }
                
                // Valid in JDK 8 and latter:
//              public void printOriginalNumbers() {
//                  System.out.println("Original numbers are " + phoneNumber1 +
//                     " and " + phoneNumber2);
//              }

                PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
                PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
                
                // Valid in JDK 8 and later:
//                  myNumber1.printOrigionNumbers();
                if (myNumber1.getNumber() == null)
                     System.out.println("First number is invalid");
                else
                    System.out.println("Frist number is " + myNumber1.getNumber());
           }
           
           public static void main(String... args) {
               validatePhoneNumber("123-456-7890","456-7890");
            }
}


本例验证一个电话号码。首先从号码中移除所有字符,数字除外;之后检查电话号码是否刚好是十位数。输出结果如下:
First number is 1234567890
Second number is invalid

访问外部类成员
一个局部类可以访问它的外部类成员,也可以访问局部变量。但是,局部类只可以访问使用final修饰的局部变量。当一个局部类访问同一个语句块中的局部变量或参数时,它要能捕获当前的变量或参数。从Java SE 8开始,局部类可以访问同一个语句块中的常量型或隐含常量型(未声明final,但值不变)的局部变量或参数。
局部类和内部类是相似的
局部类和内部类都不能定义任何static成员。在static方法中声明的局部类,它只能访问外部类的static成员。
局部类是非静态的,因为它们可以访问外部类的实例成员,也因此,它们不能包含static声明。
你不能在语句块中声明接口;接口天生是static的。
三、匿名类
匿名类可以使你的代码更简洁。你可以同时声明和实例化一个类。它们和局部类相似,但它们没有名字。如果只使用一次内部类,你可以使用匿名类。
声明匿名类
下例中,HelloWorldAnonymousClasses 在局部变量frenchGreeting和spanishGreeting初始化时,使用了匿名类;并在englishGreeting初始化时使用局部类:

public class HelloWorldAnonymousClasses {
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }
    
    public void sayHello() {
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }
        HelloWorld englishGreeting = new EnglishGreeting();
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
        
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
    // this is a function
            public void greetSomeone(String someone) { 
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String...args){
        HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
} 

匿名类的句法
一个匿名类是一个表达式。匿名类表达式的句法十分像一个构造函数的调用,除了多一个包含类定义的“{}”。
匿名类表达式由以下部分组成:

• new操作
• 一个接口名或类名,它们与匿名类的关系是被实现或被继承。上例中匿名类实现接口HelloWorld
• 包含构造函数参数的“()”,注意:接口没有构造函数,因此只用一对“()”即可。
• 匿名类的定义体,在匿名类中可以声明方法,但是不能有表达式。
访问局部变量,声明和访问匿名类成员
像局部类一样,匿名类能获取当前变量的值;它们同样可以访问同一作用域的局部变量
• 匿名类有权访问外部类的成员
• 匿名类能访问final或隐含final(Java SE 8:编译时值不变化)的局部变量
• 像嵌入类一样,匿名类中的变量可能和外部类变量名相同,从而不能直接使用变量名访问外部类变量
匿名类和局部类一样对内部的成员有规定:
• 你不能声明static初始化块或者成员接口
• 匿名类可以有常量型的static变量
在匿名类中,你可以声明以下内容:
• 字段(成员变量)
• 方法(即使它们不是父类方法的实现)
• 实例初始化块
• 局部类
但是,你无法在匿名类中声明构造函数。

四、Lambda表达式

五、何时使用嵌套类、本地类、匿名类和Lambda表达式
首先,嵌套类保证你合理地将只用在一个地方的类分组在一起。改善封装性,使代码更易读、更易维护。
• 局部类:在如果需要创建超过一个类的多个实例的情况下使用
• 匿名类:在需要声明字段或额外的方法时使用
• Lambda表达式:略
• 嵌套类:和局部类的使用条件相似,但可以类的作用域更广。
如果需要访问外部类的非公有字段和方法,使用非static嵌套类。
如果不需要访问外部类的非公有字段和方法,使用static嵌套类。
枚举类
一个枚举类型是一个特殊的数据类型,它使得一个变量可以是一系列预先定义的常量。变量值必须是这些常量中的一个。通常可以定义罗盘的方向,一个星期的各天。
因为是常量,所以枚举类型的字段都是大写字母。
在Java中,你可以使用关键字enum定义一个枚举类型。如:
public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, 
    THURSDAY, FRIDAY, SATURDAY
}


你可以在任何时候使用枚举类体表一系列的常量。如:

public class  EnumTest {
    Day day;
    
    public EnumTest (Day day) {
        this.day = day;
    }
    
    public void tellItLikeItIs() {
        switch(day) {
            case MONDAY:
                    System.out.println("Mondays are bad.");
                    break;
            case FRIDAY:    
                    System.out.println("Fridays are better. ");
                    break;
            case SATURDAY:
            case SUNDAY:
                    System.out.println("Weekends are best. ");
                    break;
             default:
                    System.out.println("Midweek days are so-so. ");
                    break;
        }
    }
    
    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();

    }
}


Java中枚举类中可以包含方法和其它字段。编译器在创建枚举时自动添加一些特别的方法。例如,它们有一个static values方法,用来返回一个包含所有枚举项的数组,并且按这些项声明的顺序。可以在上述main函数中加入如下代码,来测试:
for(Day currentDay:Day.values()){
    System.out.println(currentDay);
}

注意:所有枚举类都隐含地继承java.lang.Enum,因此Java中的枚举类不支持继承任何类。
下面的例子中,Planet是一个枚举类表示太阳系中的行星。它们都具有质量和半径的属性。每一个枚举常量都带有质量和半径参数。这些值在常量创建时被传递给构造函数,在Java中,这些常量必须在任何字段和方法之前定义。当存在字段和方法时,枚举常量必须以一个分号结束。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
注意:枚举类的构造函数必须是包私有(默认)或私有的。它自动创建定义在开始位置的常量。你不能调用枚举类的构造函数。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

根据Planet的属性和构造函数,Planet中有得到每个行星表面重力加速度和行星上物体重量的方法。这个简单的程序使用你在地球上的重量可以计算并打印出你在太阳系所有行星上的重量:

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25,5.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    private final double mass;     // in kilograms
    private final double radius;    // in meters
    Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
    }
    private double mass() { return mass; }
    private double radius() { return radius; }

    // universal gravitational constant (m3kg-1 s-2)
    public static final double G = 6.673000E-11;

    double surfaceGravity() {
return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
if(args.length != 1) {
    System.err.println("Usge: java Planet <earth_weight>");
    System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for(Planet p : Planet.values())
    System.out.printf("Your weight on %s is %f%n", p, 
    p.surfaceWeight(mass));
for(Planet p : Planet.values())
    System.out.println("The planet is " + p);

    }
}


如果运行Planet.class并使用180作为参数,可以得到下面的输出
Your weight on MERCURY is 67.996371
Your weight on VENUS is 162.899838
Your weight on EARTH is 180.000000
Your weight on MARS is 68.172693
Your weight on JUPITER is 455.500355
Your weight on SATURN is 191.882797
Your weight on URANUS is 34.479503

Your weight on NEPTUNE is 204.899053


资源来潮:Java手册-Lesson: Classes and Objects

 

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值