有时候我们不希望子类重写父类中的一些已经存在的实现,这就意味着我们需要限制继承(restrict inheritance)。
public final int topSpeed = 100; public final void stop() {};
以上代码表示:topSpeed的值不能再做修改; 没有子类可以重写/覆盖方法stop()
public final class Square {}
这个类不能再有进一步的特殊化/专门化,任何多的对Square的扩展都会导致错误。
如果我们想要追踪一个类有多少实例,该如何实现自动追踪?
不能将这个问题留给对象 也不能留给实例变量
答案是:使用类变量
final变量:不能改变值
public class Foof { final double weight = 15.6; final int whuffie; Foof(){ whuffie = 3; } }
final方法:不能被覆盖重写
public class Poof { final void calcWhuffie() { // some important things that cannot be overridden } }
final类:不能被继承
public class MyFinalClass { // class cannot be extended }
要注意区分final class和abstract class
Practice Exercise 2:
Practice Exercise 3:
通常一个对象的每个实例都有定义在类中的所有变量和方法的副本
比如矩形r1和r2都有width,height,area(), draw()等等的副本。
Example1 使用static
public class Rectangle extends Object { private static int count = 0; private int width; private int height; public Rectangle(int width, int height) { this.height = height; this.width = width; count++; } public static int getCount() { // 只能引用static变量 return count; } public int getWidth() { return width; } public int getHeight() { return height; } public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } public void draw() { System.out.println("drawing"); } public int area() { return width*height; } public static void main(String[] args) { System.out.println("There are " + Rectangle.getCount() + " rectangles."); Rectangle r1 = new Rectangle(10,20); System.out.println("There are " + Rectangle.getCount() + " rectangles."); } }
输出结果:
There are 0 rectangles.
There are 1 rectangles.
可以看到:在Rectangle类里面我们声明了变量count是static的,方法getCount是static的,那么在main()函数里面,尽管刚开始没有创建Rectangle类的对象,我们仍然可以直接使用变量count和方法getCount。
public static float E = 2.718281f; public static float PI = 3.1415926f;
为了得到值:
circleArea = Math.PI * (radius * radius);
(当然,此处仅是示例,因为这样的语句是合规的:Math.PI = 3.0f;)
我们可以使用final修饰符来保证没有人修改静态static变量
任何对静态变量的值进行修改的举动都会报错
我们已经知道了:
static final变量的初始化
注意:静态变量和实例变量是不一样的!!
Example 2
public class Bar { public static final double BAR_SIGN; static { BAR_SIGN = (double) Math.random(); } }
这里的static和后面的方法体就组成了静态初始器,用来对final static变量进行初始化。
此外也可以注意到:final static变量的命名是全为大写。
下列给出java.lang.Math声明的语句
(以上仅作学习辅助参考,没有必要将以上方法再重新声明为实例方法!)
Practice Evercise 1:
我们可以看到:当我们声明了private变量以后,对getter和setter都设置static,会导致代码编译报错。因为width和height都是private变量,那么在static静态方法里面,是无法使用他们的。静态方法只能使用静态变量。
不能被实例化的有以下:
Math类里面的函数都是静态的(全局可用)
当我们调用Math方法的时候:
Dog myDog = new Dog(); myDog.catchBall();
此处调用了一个非静态的方法,需要在之前使用一个引用变量的名字(此处是myDog)
Math.max(10,20);
调用一个静态方法,需要使用类名
Random 类是java.util包的一部分,提供能够随机生成随机数的方法
Example:
import java.util.Random; public class RandTest { public static void main(String[] args) { Random r = new Random(); float aRandomFloat = r.nextFloat(); int aRandomInt = r.nextInt(); System.out.println("A random float is " + aRandomFloat); System.out.println("A random float is " + aRandomInt); } }
某次的运行结果是:
A random float is 0.38243788
A random float is 874289547
其中r.nextFloat()就是随机获取一个浮点数,r.nextInt()就是随机获取一个整数
包装类(wrapping class):当基本类型的变量需要作为对象处理是使用
每个原始类型都有一个包装类
包装类是java.lang包的一部分,无需import他们
Boolean Integer Character Byte Short Long Float Double
其中要注意 Integer和Character是不同于基本类型的名字,其他都是直接将基本类型首字母大写。
包装vs不包装
int i = 10; // 包装 Integer iWrapped = new Integer(i); // 非包装 int unWrapped = iWrapped.intValue();
其中,所有的包装类都有着相似的方法,比如,charValue(),booleanValue()
自动包装(从java 5.0之后):从基本类型到包装对象的转换是自动的。
在java5.0之前,基本类型的变量和对象引用变量从来不能互换处理
下面我们来看一下在java5.0之前和之后对原始变量的ArrayList里面的一个int变量的包装和解包装过程:
在这里插入代码片
(有问题 暂后)
使用自动包装
方法参数:你可以传递一个引用或者一个对应的基本类型到接收包装类型的方法里,反之亦然。
void takeNumber (Integer i) { }
int x = 1; takeNumber(x);
该方法的参数是Integer,但是我们可以把int类型的参数传递进去
返回值:你可以在一个返回基本类型的方法里返回一个引用或者一个对应的基本类型,反之亦然。
int giveNumber () { return x; }
Integer x =1; int x = 1;
布尔表达式: 在需要布尔表达式的地方,你可以使用表达式计算一个布尔值,一个基本类型的变量或者一个对应的包装类型。
if(boolean) { System.out.println("true"); }
if(10>9) { boolean x = true; Boolean x = true;
对数字的操作:在原本期望使用基本类型的操作中,可以使用包装类型
Integer i = new Integer(10); i++; System.out.println("New value is: " + i);
赋值:声明为包装类型的变量(或者基本类型)可以被赋值对应的包装类型(或者基本类型)
// 第1种 int x =10; Integer d = x;
// 第2种 Integer x =10; int d = x;
包装器有解析方法(parse methods),他接受一个String并返回一个基本类型的变量
Example:
String str1 = "10"; String str2 = "123.45"; String str3 = "true"; int i = Integer.parseInt(str1); double d = Double.parseDouble(str2); // boolean b = new Boolean(str3).booleanValue(); // 这种是课件里写的 // 但是编译器会提醒“The constructor Boolean(String) is deprecated since version 9” boolean b = Boolean.parseBoolean(str3);
String anotherStr = "ten"; int anotherInt = Integer.parseInt(anotherStr);
这里能够通过编译但是无法运行,产生NumberFormatException错误,导致无法被解析(parsed)
静态导入:当使用静态方法或者变量的时候,可以导入来节省输入时间
import java.lang.Math; public class BeforeStaticImports { public static void main(String[] args) { System.out.println("Square root is " + Math.sqrt(4.0)); } }
import static java.lang.System.out; import static java.lang.Math.*; class WithStaticImports { public static void main(String[] args) { out.println("Square root is \" + Math.sqrt(4.0)); } }
但是使用静态导入会导致代码可读性变差
所有的包装器类都是Number抽象类的子集(java.lang包的一部分)
(图 see java API)
比如,我们可以构造一个Integer类型的Number对象:
Number num = new Integer(10);
在这些情况下,我们使用Number对象而不是基本变量:
当方法的参数需要一个对象的时候,比如, void takeNumber(Integer i){}
通常在处理数字集合的时候使用
Number的子类提供常量,比如来表示相应数据类型的上界和下界(MIN_VALUE & MAX_VALUE)
System.out.println(Long.MIN_VALUE);
得到负的2的63次方
可以使用类方法将值转换到或者从其他基本类型,转换到或者从字符串,或者在不同数值之间转换
public class IntegerDemo { public static void main(String[] args) { int i = 181; System.out.print("Number = " + i); System.out.print("Hex value = " + Integer.toHexString(i)); } }
Practice Exercise 4
public class Round { public static void main(String[] args) { System.out.print(Math.round(-0.5) + " " + Math.round(0.5)); } }
输出结果0 1
以下哪些类定义了不可改变的对象
Character Byte Short Object
一般情况下,方法可以调用其他的方法,当然也有些是例外的,比如静态方法不能调用非静态方法
递归:直接或者间接的调用自己的方法
递归方法只知道如何解决最简单的问题,就是所谓的基本情况
递归算法
java实现阶乘:
public class CalculateFactorial { public long factorial(long number) { if(number <= 1) { return 1; } else { return number*factorial(number-1); } } }
递归:另一种使用重复而不使用循环控制的程序控制形式
有时可以明确简单的说明解决问题的方法
但是会给程序带来额外的开销:每次程序调用递归方法的时候,需要为方法的局部变量和参数分配空间:需要额外的内存+管理额外空间需要的时间
Practice Exercise 5:
第一问:mystery(1, 7)的结果是7
第二问:最终返回的是a和b的乘积