6.面向对象(中)

来源:互联网 发布:黄金储备数据 编辑:程序博客网 时间:2024/06/05 22:39

重点理解抽象类和接口的联系与区别:

抽象类主要作为多各类的模板   接口定义了多类应该遵守的规范

一.Java8增强的包装类

1.Integer包装类

Integer a = 2;Integer b = 2;System.out.println(a == b);//trueInteger aa = 128;Integer bb = 128;System.out.println(aa == bb);//false
注意:系统把-128~127之间的整数自动装箱成Integer实例,并放入了一个名为cache的数组中缓存起来;如果以后把一个-128~127之间的整数自动装箱成一个Integer实例,实际上直接指向对应的数组元素,永远都是引用cache数组的同一个数组元素,所以相等;

二.处理对象

1.打印对象和toString()方法

Object类提供的toString()方法:"类名+@+hashCode",所以一般要重写这个方法;

2.==和equals()方法

==:

a.如果两个变量是基本类型,且都是数值类型(不一定要求数据类型严格相同),则只要两个变量的值相等,就返回true;

b.如果是两个引用类型变量,只有他们指向同一个对象时,==才返回true;

重点:"hello"直接量和new String("hello")区别?

当Java程序直接使用"hello"字符串直接量时(包括可以再编译时就计算出来的字符串值)时,JVM将会使用常量池来管理这些字符串;

当使用new String("hello")是,JVM会先使用常量池来管理"hello"直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中,换句话说,new String("hello")一个产生了两个字符串对象!

equals():

equals()方法是Object类提供的一个实例方法,判断两个对象相等的标准与==运算符没有区别;所以意义不大,最好采用重写equals()方法,String类就重写了equals()方法;

三.类成员

static关键字修饰的成员就是类成员,有类变量,类方法,静态初始化块,static关键字不能修饰构造器.static修饰的类成员属于整个类,不属于单个实例;

1.理解类成员

a.Java类里可以包含的5种成员:变量   方法   构造器   初始化块   内部类(包括接口,枚举类);

b.对static关键字而言,有一条非常重要的规则:

类成员不能访问实例成员,因为类成员属于类的,类成员的作用域比实例成员的作用域更大,完全可能出现类成员已经初始化完成,但实例成员还不曾初始化的情况,如果允许类成员访问实例成员将会引起大量的错误!

2.单例类(Singleton)

如果一个类始终只能创建一个实例,则这个类被称为单例类:

根据良好封装的原则:

一旦把该类的构造器隐藏起来,就要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰(因为调用改方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类);

除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾创建过改对象,也就无法保证只创建一个对象;所以需要提供一个成员变量来保存曾经创建的对象,因为该成员变量需要被上面的静态方法访问,所以该成员变量必须使用static修饰;

四.final修饰符

final关键字可用于修饰类,变量和方法;

final修饰变量时,表示该变量一旦获得了初始值就不可被改变,final既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量,形参;

严格的说,final修饰的变量不可被改变,一旦获得了初始值,该final变量的值就不能被重新赋值;

1.final成员变量

a.成员变量是随类初始化或对象初始化而初始化的.当类初始化时,系统会为该类的类变量分配内存,并分配默认值;当创建对象时,系统会为该对象的实例变量分配内存,并分配默认值;

b.对于final修饰的成员变量而言,一旦有了初始值,就不能被重新赋值;

c.final修饰的成员变量必须由程序显式的指定初始值;

总结:final修饰的类变量,实例变量能指定初始值的地方如下:

I:类变量:1️⃣静态块    2️⃣声明时

II:实例变量:1️⃣普通快   2️⃣声明时   3️⃣构造器

注意点1:

如果普通初始化块已经为某个实例变量指定了初始值,则不能再在构造器中为改实例变量指定初始值;

final修饰的类变量,要么在定义该类变量时指定初始值,要么在静态初始化块中为该类变量指定初始值;

注意点2:

与普通成员变量不同的是,final成员变量(包括实例变量和类变量)必须由程序员显式初始化,系统不会对final成员进行隐式初始化;

2.final局部变量

a.系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化;

b.如果final修饰的局部变量在定义时没有指定默认值,则可以再后面代码中对该变量赋初始值,但只能一次,不能重复赋值;如果final修饰的局部变量在定义时已经指定默认值,则后面代码中不能再对该变量赋值;

c.不能对final修饰的形参赋值;

final修饰局部变量 --> 修饰形参:不能赋值

                                   非形参:若声明时赋值了,则以后不能赋值

                                               若声明时未赋值,则只能赋值一次

3.final修饰基本类型变量和引用类型变量的区别

当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变;

当使用final修饰引用了诶西变量时,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变;

4.可执行"宏替换"的final变量

满足一下三个条件,这个final变量就不是一个变量,相当于一个直接量:

1️⃣.使用final修饰符修饰;

2️⃣.在定义该final变量时指定了初始值;

3️⃣.该初始值可以再编译时就被确定下来;

//定义一个普通局部变量final int a = 5;System.out.println(a);
对于这个程序来说,变量a其实不存在,当程序执行System.out.println(a);时,实际转换为执行System.out.println(5);

总结:

a.当定义final变量时就为该变量指定了初始值,而且该初始值可以再编译时就确定下来那么这个final变量本质上就是一个"宏变量",编译器会把程序中所有用到该变量的地方直接替换成该变量的值;

b.除了为final变量赋值赋直接量的情况外,如果被赋的表达式知识基本的算术表达式或字符串连接运算符,没有访问普通变量,调用方法,Java编译器同样会将这种final变量当成"宏变量"处理;

c.Java会使用常量池来管理曾经用过的字符串直接量,String a = "java";执行这个语句后,常量池就会缓存一个字符串"java";如果程序再次执行String b = "java',系统将会让b直接指向常量池中的"java"字符串,因此a == b返回true;

String s1 = "疯狂Java";//s2变量引用的字符串可以再编译时就确定下来//因此s2直接引用常量池中已有的"疯狂Java"字符串String s2 = "疯狂" + "Java";System.out.println(s2 == s2);//true//定义2个字符串直接量String str1 = "疯狂";String str2 = "Java";String str3 = str1 + str2;System.out.println(s1 == s3);//false
对于s3而言,由str1和str2进行连接运算后得到.由于str1,str2是两个普通变量,编译器不会执行"宏替换",因此编译器无法在编译时就确定s3的值,也就无法让s3指向字符串池中缓存的"疯狂Java" --> s1 == s3 为false;

让s1 == s3 为true的话,只要让编译器对str1,str2执行宏替换,这编译器就可以再编译阶段确定s3的值,即对str1,str2用final进行修饰;

d.对于final实例变量而言,只有在定义该变量时指定初始值才有"宏替换"的效果;

5.final方法

a.final修饰的方法不可被重写,如果不希望子类重写父类的某个方法,可以使用final修饰该方法;

b.对于一个private方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法-----如果子类中定义了一个与父类private方法有相同方法名,相同形参列表,相同返回值类型的方法,也不是方法重写------只是重新定义了一个新方法!

c.final修饰的方法仅仅是不能被重写,完全可以被重载;

6.final类

a.final修饰的类不可以有子类,即final修饰的类不可被继承;

7.不可变类

a.定义:

不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的;

如果要创建自定义的不可变类可遵守如下规则:

→使用private和final修饰符来修饰该类的成员变量

→提供带参构造器,来初始化成员变量

→仅仅提供getter方法

→如果有必要,重写Object类的hashCode(),equals()方法;

b.与不可变类对应的是可变类,可变类的含义是该类的实例变量是可变的;

8.缓存实例的不可变类

如果某个对象需要频繁的重复使用,缓存该实例就利大于弊,比如Integer就缓存了-128~127的Integer对象;

五.抽象类

定义一个Shape类,这个类提供计算周长calPerimeter()方法,但是不同的子类对周长的计算方法不一样,该如何处理?

方案1:既然Shape类不知道如何实现calPerimeter()方法,就不要管了,这不行,加入有一个Shape引用变量,该变量实际引用Shape子类的实例,那么这个Shape变量就无法调用calPerimeter()方法,必须将其强制类型转换为其子类类型,才可调用calPerimeter()方法,不灵活;

方案2:通过定义抽象方法;

1.抽象方法和抽象类

a.抽象方法和抽象类必须使用abstract修饰来定义,有抽象方法的类只能被定义为抽象类,抽象类可以没有抽象方法;

抽象方法,抽象类的规则:

→抽象类,抽象方法必须使用abstract来修饰,抽象方法不能有方法体;

→抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例;

→抽象类可以包含成员变量,方法(普通方法和抽象方法都可以),构造器,初始化块,内部类(接口,枚举);

→含有抽象方法的类,或实现了一个接口,但没有完全实现接口包含的抽象方法的类,只能被定义为抽象类;

b.利用抽象类和抽象方法的优势,可以更好的发挥多态的优势;

当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表面这个方法必须由子类提供实现(重写),而final修饰的类不能被继承,修饰的方法不能被重写,因此final和abstract永远不能同时使用;

c.static和abstract不能同时修饰方法,可以同时修饰内部类;

d.private和abstract不能同时修饰方法;

2.抽象类的作用

a.从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,避免子类设计的随意性

b.提供一个抽象类,父类提供了多个子类的通用方法,并把一个或多个方法留给子类实现,局势模板模式;

六.Java8改进的接口

抽象类是从多个类抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的"抽象类"-----接口.接口不能包含普通方法,所有的方法都是抽象方法.Java8对接口进行了改造,允许在接口中定义默认方法,默认方法可以提供方法实现;

1.接口的概念

a.接口是从多个相似类中抽象出来的规范,接口不提供任何实现,因此,接口定义的是多个类共同的公共行为规范;

2.Java8中接口的定义

a.由于接口定义的是一种规范,因此接口不能包含构造器和初始化块,接口里面可以包含成员变量(只能是静态常量),方法(抽象实例方法,类方法,默认方法),内部类(内部接口,枚举);

b.因为接口里定义的是多个类共同的公共行为规范,因此接口里的所有成员,包括常量,方法,内部类和内部枚举都是public访问权限;

c.不管是否使用public static final修饰符,接口里的成员变量总是使用者三个修饰符来修饰;

//系统自动为接口里定义的成员变量增加public static final 修饰符int MAX_SIZE = 50;public static final int MAX_SIZE = 50;//上面两行代码的结果完全一样
d.修饰符:

成员变量:public static final

方法:public abstract      public  static       public  default

//默认增加public abstractvoid getData();//默认方法,需要增加default修饰,并且默认增加publicdefault void print(String str){  System.out.println("111");}//类方法,需要增加static修饰,冰清默认增加publicstatic String test(){   System.out.println("2");}
由于默认方法并没有static修饰,因此不能直接使用接口来调用默认方法,需要使用接口的实现类的实例来调用默认方法;

类方法可以直接使用接口来调用;

3.接口的继承

接口的继承和类继承不一样,接口完全支持多继承,即一个接口可以由多个直接父接口;

4.使用接口

作用:

→定义变量,也可用于进行强制类型转换;

→调用接口中定义的常量;

→被其他类实现;

实现接口与继承父类相似,一样可以获得所实现接口里定义的常量(成员变量),方法(包括抽象方法和默认方法);

5.接口和抽象类

接口:作为系统与外界交互的窗口,接口体现的是一种规范;

抽象类:抽象类作为系统中多个子类的共同父类,它所体现的是一种模板式设计;

6.面向接口编程



0 0
原创粉丝点击