Day10
今天学习方法重载,单例模式,final关键字与包装类。
1.方法重载
在一个类中,方法名一样,但是参数列表(类型,数量,顺序)不同,方法重载,体现出OOP中的多态。
构造方法的重载:语法要求与普通方法的重载类似
一个构造方法可以调用重载形式的另外的构造方法:
语法: this(…); 只能出现在构造方法中,表示调用其他形式的构造方法。
说明:
1.参数列表不同包括:个数不同、顺序不同、类型不同。
注:仅仅参数变量名称不同是不可以的。
2.跟成员方法一样,构造方法也可以重载。
3.声明为final的方法不能被重载。
4.声明为static的方法不能被重载,但是能够被再次声明。
方法的重载的规则:
1.方法名称必须相同。
2.参数列表必须不同。
3.方法的返回类型可以相同也可以不相同。
4.仅仅返回类型不同不足以称为方法的重载。
2.设计模式之单例模式,
如果要求一个JVM中只允许有一个实例,这里说两种最简单的实现方式:
实现1:懒汉式(线程不安全)
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();//在第一次调用的时候实例化自己
}
return singleton;
}
}
实现2:饿汉式(性能问题)
public class Singleton {
private Singleton() {
}
//在类初始化时,已经自行实例化
private static Singleton singleton = new Singleton2();
public static Singleton getInstance() {
return singleton;
}
}
3.final关键字
3.1 修饰变量
修饰属性(全局变量):可以在声明时赋值,或者构造方法赋值,或者在代码块中赋值。
修饰局部变量:方法参数,方法体中声明的变量。
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,也就是地址不可更改,但要注意的是地址中的内容是可以改变的。
变量不可更新其值,所谓的常量。
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
3.2 修饰方法:表示方法不可被子类重写
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
3.3 修饰类:表示类不可被继承
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
3.4final和static
很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。看下面这个例子:
public class Test {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
System.out.println(myClass1.i);
System.out.println(myClass1.j);
System.out.println(myClass2.i);
System.out.println(myClass2.j);
}
}
class MyClass {
public final double i = Math.random();
public static double j = Math.random();
}
运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。从这里就可以知道final和static变量的区别了。
4.包装类
为什么需要包装类(Wrapper Class)
java并不是纯面向对象的语言,java语言是一个面向对象的语言,但是java中的基本数据类型却不是面向对象的,但是我们在实际使用中经常将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象!所以Java 设计开始就提供了 8 种 基本数据类型及对应的 8 种包装数据类型。
包装类对基本数据进行封装,提供了若干方法对其操作,还有一些静态方法提供公共功能。
int Integer
short Short
byte Byte
long Long
char Character
float Float
double Double
boolean Boolean
包装类创建对象的方式就跟其他类一样。
Integer num1=new Integer(10);
自动装箱:基本数据类型可以自动转换为对应的包装类型
自动拆箱:包装类型可以自动转换为对应的基本类型
Integer num1 = 10; //自动装箱
int num2 = num1; //自动拆箱
包装类中的缓存机制
创建包装类对象有两种方式:new关键字、valueOf()方法。
只要Integer类第一次被使用到,Integer的静态内部类就被加载,加载的时候会创建-128到127的Integer对象,同时创建一个数组cache来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已经缓存的对象,也就是说不会再新建对象;当使用new关键字or使用valueOf()方法创建小于-128大于127的值对象时,就会创建新对象。
所以我们使用包装类直接创建-128到127之间的对象时地址是相同。
public class Test {
public static void main(String[] args) {
Integer num1 = 10;
Integer num2 = 10;
Integer num3 = new Integer(10);
Integer num4 = new Integer(10);
Integer num5 = 128;
Integer num6 = 128;
System.out.println(num1 == num2);
System.out.println(num3 == num4);
System.out.println(num5 == num6);
}
}
因此上面运行结果是true,false,false。
此外,在8种包装类型中,有缓存区的有Character、Byte、Short、Integer、Long,而且它们的实现方式基本一样,都是-128到127的缓存范围。Boolean虽然没有缓存区,但是因为只有两个值true、false,所以Boolean在成员变量中就创建了两个相应的对象。
没有缓存区的只有Float、Double,之所以没有原因很简单,即便是0到1这么小的范围,浮点数也有无数个,使用缓存区缓存它们不具备可能性和实用性。
缓存区的存在使得常用的包装类对象可以得到复用,这有利于提升性能。当我们需要创建新对象的时候再new一个,增加了灵活性。