Java第八章(面向对象高级)

一、Static关键字

  • 使用范围:
    • 在Java类中,可用static修饰属性、方法、代码块、内部类
  • 被修饰后的成员具备以下特点:
    • 随着类的加载而加载
    • 优先于对象存在
    • 修饰的成员,被所有对象所共享
    • 访问权限允许时,可不创建对象,直接被类调用

1.静态变量的特点

非static的成员变量是实例变量、非静态变量

使用static修饰的成员变量就是静态变量(或类变量、类属性),存放在堆空间内。

  • 静态变量的默认值规则和实例变量一样。
  • 静态变量值是所有对象共享。
  • 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用。
  • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。
  • 静态变量的get/set方法也静态的,当局部变量与静态变量重名时,使用“类名.静态变量”进行区分。

2.  对比静态变量和实例变量

① 个数

 >静态变量:在内存空间中只有一份,被类的多个对象所共享。

>实例变量:类的每一个实例(或对象)都保存着一份实例变量。

②内存位置

>静态变量:jdk6及之前,存放在方法区(永久代)。jdk7及之后,存放在堆空间。方法区叫元空间。

>实例变量:存放在堆空间的对象实体中。

③加载时机

 >静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。

>实例变量:随着对象的创建而加载。

④调用者

 >静态变量:可以被类直接调用,也可以使用对象调用。

>实例变量:只能使用对象进行调用。

⑤判断是否可以调用 --->从生命周期的角度解释

                             类变量             实例变量 

类                           yes                       no

对象                       yes                       yes

        类变量是和类一起加载的,也就是说类和类变量是先构造完成的,对象和实例变量是后构造的,如果实例对象还没创建,类就不能调用实例变量

⑥消亡时机

 >静态变量:随着类的卸载而消亡。

>实例变量:随着对象的消亡而消亡

3.静态变量的内存解析

4.静态方法的特点

用static修饰的成员方法就是静态方法(类方法)。

  • 静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。
  • 只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。
  • 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。也不能使用this和super。
  • 静态方法可以被子类继承,但不能被子类重写。不存在多态性。可以重载。
  • 静态方法的调用都只看编译时类型。
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有this,也不能有super。如果有重名问题,使用“类名.”进行区别。
  •                              类方法             实例方法 

    类                           yes                       no

    对象                       yes                       yes

补充:在类的非静态方法中,可以调用当前类中的静态结构(属性,方法)或非静态结构(属性,方法)。即:静态只能调用静态,非静态都能调

5.开发中,什么时候需要将属性声明为静态的?

        >判断当前类的多个实例是否能共享此成员,且此成员变量的值是相同的。

        >开发中,常将一些常量声明是静态的。比如:Math类中的PI.

6.开发中,什么时候需要将方法声明为静态的?

        >方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法。

        >开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类。

是否可以从一个static方法内部发出对非static方法的调用?

        只能通过对象来对非静态方法的调用。如main方法中调用非static方法需要创建对象。

被static修饰的成员(类、方法、成员变量)能否再使用private进行修饰?

        完全可以。除了代码块,代码块本身不能用权限修饰符修饰

二、单例(Singleton)设计模式

        设计模式是在大量的实践中总结理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路"

经典的设计模式共有23种。每个设计模式均是特定环境下特定问题的处理方法。

        所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

      ★  如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的

①单例模式的两种实现方式

饿汉式
class Singleton {
    // 1.私有化构造器
    private Singleton() {
    }

    // 2.内部提供一个当前类的实例
    // 4.此实例也必须静态化
    private static Singleton single = new Singleton();

    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        return single;
    }
}
懒汉式
class Singleton {
    // 1.私有化构造器
    private Singleton() {
    }
    // 2.内部提供一个当前类的实例
    // 4.此实例也必须静态化
    private static Singleton single;
    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        if(single == null) {
            single = new Singleton();
        }
        return single;
    }
}
饿汉式 vs 懒汉式

饿汉式:

  • 特点:立即加载,即在使用类的时候已经将对象创建完毕。

  • 优点:实现起来简单;没有多线程安全问题。

  • 缺点:当类被加载的时候,会初始化static的实例,静态对象被创建并分配内存空间,从这以后,这个static的实例便一直占着这块内存,直到类被卸载时,静态变量被摧毁,并释放所占有的内存。因此在某些特定条件下会耗费内存

懒汉式:

  • 特点:延迟加载,即在调用静态方法时实例才被创建。

  • 优点:实现起来比较简单;当类被加载的时候,static的实例未被创建并分配内存空间,当静态方法第一次被调用时,初始化实例变量,并分配内存,因此在某些特定条件下会节约内存

  • 缺点:在多线程环境中,这种实现方法是完全错误的,线程不安全,根本不能保证单例的唯一性。

三、代码块

        作用:用来初始化类或对象的信息(即初始化类或对象的成员变量)

静态代码块

  1. 可以有输出语句。

  2. 可以对类的属性、类的声明进行初始化操作。

  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。

  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。

  5. 静态代码块的执行要先于非静态代码块。

  6. 静态代码块随着类的加载而加载,且只执行一次。

非静态代码块

  1. 可以有输出语句。

  2. 可以对类的属性、类的声明进行初始化操作。

  3. 除了调用非静态的结构外,还可以调用静态的变量或方法。

  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。

  5. 每次创建对象的时候,都会执行一次。且先于构造器执行。

实例变量赋值顺序

① 默认初始化

② 显式初始化   或 ⑤代码块中初始化

③ 构造器中初始化

④ 通过"对象.属性"或"对象.方法"的方式,给属性赋值

顺序:① - ②/⑤ - ③ - ④

静态代码块,普通代码块,构造方法,从类加载开始的执行顺序?

        静态代码块 --> 普通代码块 --> 构造器

四、final关键字

 final修饰类

        表示这个类不能被继承,没有子类。提高安全性,提高程序的可读性。

final修饰方法

        表示这个方法不能被子类重写。

final修饰变量

        final修饰某个变量(成员变量或局部变量),一旦赋值,它的值就不能被修改,即常量,常量名建议使用大写字母。

可以显式赋值、或在初始化块赋值、实例变量还可以在构造器中赋值

排错

public class Something {
    public int addOne(final int x) {
        return ++x; //错误
        // return x + 1; //正确,x的值没变
    }
}

排错

public class Something {
    public static void main(String[] args) {
        Other o = new Other();
        new Something().addOne(o);
    }
    public void addOne(final Other o) {
        // o = new Other();  //错误,o被final修饰
        o.i++;  //正确
    }
}
class Other {
    public int i;
}
final不能用于修饰构造方法

没有意义

五、抽象类与抽象方法---abstract关键字

Java语法规定,包含抽象方法的类必须是抽象类

抽象类:被abstract修饰的类。

抽象方法:被abstract修饰没有方法体的方法。其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体),如下方代码findArea的功能就是求面积,只是没有具体实现细节。

注意:抽象方法没有方法体

说明:

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  • 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
  • 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。

理解:子类的构造方法中,有默认的super()或手动的super(实参列表),需要访问父类构造方法。

  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

        抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

  • 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

注意

  • 不能用abstract修饰变量、代码块、构造器;
  • 不能用abstract修饰私有方法、静态方法、final的方法、final的类。

>私有方法不能被重写,而abstarct修饰的方法必须要被重写

>避免静态方法使用类进行调用,因为abstarct修饰的方法不能被调用。

>final的方法不能被重写,抽象方法一定要重写

>final的类不能有子类,而抽象类必须有子类来造对象。

六、接口

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a关系。

接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。

接口使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

引用数据类型:数组,类,枚举,接口,注解,记录。

1.接口内部结构的说明:

>可以声明:

        属性:必须使用public static final修饰

        方法:jdk8之前,声明抽象方法,修饰为public abstract

                   jdk8:声明静态方法、默认方法

                   jdk9:声明私有方法

>不可以声明:构造器、代码块等

2. 接口的使用规则

class A extends SuperA implements B,C{} 

A相较于SuperA来讲,叫做子类。

A相较B,C来讲,叫做实现类。

①接口不能创建对象,但是可以被类实现(implements ,类似于被继承)。

②类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类。

③对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

格式:【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。

④接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次

⑤一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。

接口中声明的静态方法只能被接口来调用,不能使用其实现类进行调用。

⑦接口中声明的默认方法可以被实现类继承,实现类在没有重写此方法的情况下,默认调用接口中声明的默认方法。如果实现类重写了此方法,则调用的是自己重写的方法。

⑧类实现了两个接口,而两个接口中定义了同名同参数的默认方法,则实现类在没有重写此两个接口默认方法的情况下,会报错 ------>接口冲突 

        要求:此时实现类必须要重写接口中定义的同名同参数的方法。

⑨子类(或实现类)继承了父类并实现了接口,父类和接口中声明了同名同参数的方法。(其中,接口中的方法是默认方法default)。默认情况下,子类(或实现类)在没有重写此方法的情况下调用的是父类中的方法。 ------>类优先原则

3.接口与实现类对象构成多态引用

        实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你new的实现类对象实现的方法体。

接口名 变量名 = new 实现类的对象

4.接口与抽象类之间的对比

访问接口的默认方法如何使用

        使用实现类的对象进行调用。而且实现还可以重写此默认方法。

七、内部类(难)

>成员内部类:直接声明在外部类的里面。

        >使用static修饰的,静态的成员内部类

        >不使用static修饰的,非静态的成员内部类

>局部内部类:声明在方法内、构造器内、代码块内的内部类

        >匿名的局部内部类

        >非匿名的局部内部类

八、枚举类

枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。

1.enum方式定义的要求和特点

  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
  • 列出的实例系统会自动添加 public static final 修饰。
  • 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
  • 编译器给枚举类默认提供的是private的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数
  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的private可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
  • 枚举类默认继承的是java.lang.Enum类,因此不能再继承其他的类型。
  • JDK5.0 之后switch,提供支持枚举类型,case后面可以写枚举常量名,无需添加枚举类作为限定。

举例:

public enum SeasonEnum {
    SPRING("春天","春风又绿江南岸"),
    SUMMER("夏天","映日荷花别样红"),
    AUTUMN("秋天","秋水共长天一色"),
    WINTER("冬天","窗含西岭千秋雪");

    private final String seasonName;
    private final String seasonDesc;
    
    private SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
}

2.enum中常用方法

String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
    
static 枚举类型[ ] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法
    
static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
    
String name():得到当前枚举常量的名称。建议优先使用toString()。
    
int ordinal():返回当前枚举常量的次序号,默认从0开始

3.实现接口的枚举类

  • 和普通 Java 类一样,枚举类可以实现一个或多个接口
  • 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
  • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法

举例:

interface Info{
	void show();
}

//使用enum关键字定义枚举类
enum Season1 implements Info{
	//1. 创建枚举类中的对象,声明在enum枚举类的首位
	SPRING("春天","春暖花开"){
		public void show(){
			System.out.println("春天在哪里?");
		}
	},
	SUMMER("夏天","夏日炎炎"){
		public void show(){
			System.out.println("宁静的夏天");
		}
	},
	AUTUMN("秋天","秋高气爽"){
		public void show(){
			System.out.println("秋天是用来分手的季节");
		}
	},
	WINTER("冬天","白雪皑皑"){
		public void show(){
			System.out.println("2002年的第一场雪");
		}
	};
	
	//2. 声明每个对象拥有的属性:private final修饰
	private final String SEASON_NAME;
	private final String SEASON_DESC;
	
	//3. 私有化类的构造器
	private Season1(String seasonName,String seasonDesc){
		this.SEASON_NAME = seasonName;
		this.SEASON_DESC = seasonDesc;
	}
	
	public String getSEASON_NAME() {
		return SEASON_NAME;
	}

	public String getSEASON_DESC() {
		return SEASON_DESC;
	}
}

九、注解(Annotation)

注解(Annotation)是从JDK5.0开始引入,以“@注解名”在代码中存在。例如:

@Override

@Deprecated

@SuppressWarnings(value=”unchecked”)

Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。

注解可以在类编译、运行时进行加载,体现不同的功能。

1.注解与注释

对于单行注释和多行注释是给程序员看的。

而注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同,做出相应的处理。

2.三个最基本的注解

@Override
  • 用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误!
  • 只能标记在方法上。
  • 它会被编译器程序读取。
 @Deprecated
  • 用于表示被标记的数据已经过时,不推荐使用。
  • 可以用于修饰 属性、方法、构造、类、包、局部变量、参数。
  • 它会被编译器程序读取。
@SuppressWarnings
  • 抑制编译警告。当我们不希望看到警告信息的时候,可以使用 SuppressWarnings 注解来抑制警告信息
  • 可以用于修饰类、属性、方法、构造、局部变量、参数
  • 它会被编译器程序读取。
  • 可以指定的警告类型有(了解)
    • all,抑制所有警告
    • unchecked,抑制与未检查的作业相关的警告
    • unused,抑制与未用的程式码及停用的程式码相关的警告
    • deprecation,抑制与淘汰的相关警告
    • nls,抑制与非 nls 字串文字相关的警告
    • null,抑制与空值分析相关的警告
    • rawtypes,抑制与使用 raw 类型相关的警告
    • static-access,抑制与静态存取不正确相关的警告
    • static-method,抑制与可能宣告为 static 的方法相关的警告
    • super,抑制与置换方法相关但不含 super 呼叫的警告
    • ...

3.元注解

对现有的注解进行解释说明。

  • @Target:表明可以用来修饰的结构

  • @Retation:表明生命周期

十、包装类

        为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性,继承性,多态性),我们给各个基本数据类型的变量都提供了对应的包装类。

     Java针对八种基本数据类型定义了相应的引用类型:包装类(封装类)。有了类的特点,就可以调用类中的方法,Java才是真正的面向对象。

1.封装以后的,内存结构对比:

public static void main(String[] args){
	int num = 520;
	Integer obj = new Integer(520);
}

2.包装类与基本数据类型间的转换

        装箱:把基本数据类型转为包装类对象

调用valueof(xxx)方法

Integer obj1 = new Integer(4);//使用构造函数函数
Float f = new Float(“4.56”);
Long l = new Long(“asdf”);  //NumberFormatException

Integer obj2 = Integer.valueOf(4);//使用包装类中的valueOf方法
        拆箱:把包装类对象拆为基本数据类型

调用xxxValue()方法

Integer obj = new Integer(4);
int num1 = obj.intValue();
      自动装箱与拆箱:

由于我们经常要做基本类型与包装类之间的转换,从JDK5.0开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:

Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。

注意:只能与自己对应的类型之间才能实现自动装箱与拆箱。

Integer i = 1;
Double d = 1;//错误的,1是int类型

3.基本数据类型、包装类与字符串间的转换

(1)基本数据类型转为字符串

方式1:调用字符串重载的valueOf()方法

int a = 10;
//String str = a;//错误的

String str = String.valueOf(a);

方式2:更直接的方式

int a = 10;

String str = a + "";

(2)字符串转为基本数据类型

调用包装类的静态方法:parseXxx()

String s1 = "123";

int i1 =Integer.parseInt(s1);

sout.(i1 + 10);

面试题:

public void method1() {
    Integer i = new Integer(1);
    Integer j = new Integer(1);
    System.out.println(i == j);  //false
//底层都会调用Integer的valueOf方法
    Integer m = 1;
    Integer n = 1;
    System.out.println(m == n); //true
-128~127
    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y);  //false
}​​​​​​​

包装类

缓存对象

Byte

-128~127

Short

-128~127

Integer

-128~127

Long

-128~127

Float

没有

Double

没有

Character

0~127

Boolean

true和false

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值