Java中的关键字

1.this关键字

构造器是创建Java对象的途径,是不是说构造器完全负责创建Java对象?

答:不是!构造器是创建Java对象的重要途径,通过new关键字调用构造器时,构造器也确实返回了该类的对象,但这个对象并不是完全由构造器负责创建的。实际上,当程序员调用构造器时,系统会先为该对象分配内存空间,并为这个对象执行默认初始化,这个对象已经产生了。也就是说,当系统开始执行构造器的执行体之前,系统已经创建了一个对象,只是这个对象还不能被外部程序访问,只能在该构造器中通过this来引用。当构造器的执行体执行结束后,这个对象作为构造器的返回值被返回,通常还会赋给另一个引用类型的变量,从而让外部程序可以访问该对象。—《疯狂Java讲义》

一、对象创建的过程和this的本质:
构造方法是创建Java对象的重要途径,通过new关键字调用构造器时,构造器也确实返回该类的对象,但这个对象并不是完全由构造器负责创建。创建一个对象分为如下四步:

  1. 分配对象空间,并将对象成员变量初始化(默认初始化)。
  2. 执行属性值的显示初始化(private String name=“刘瘦瘦”)。
  3. 执行构造方法。
  4. 返回对象的地址给相关变量。

this的本质就是“创建好的对象的地址”(对象的默认引用)!由于在构造方法调用前,对象已经创建。因此在构造方法中也可以使用this代表当前对象。
二、this关键字的意义
根据this出现位置的不同,this作为对象的默认引用有两种情形:

  1. 构造器中引用该构造器正在初始化的对象;
  2. 在方法中引用调用该方法的对象。

三、this关键字的作用
1.当成员变量和局部变量重名,可以用this区分。

public class This {
	private String name;
	private int age;
	
	public This(String name,int age){
		//成员变量名和局部变量重名,可以用关键字this区分
		this.name=name;
		this.age=age;
	}
}

2.this关键字调用重载的构造方法,避免相同的初始化代码。但是,只能在构造方法中用,并且必须位于构造方法的第一句。

class TestThis {
	private String name;
	private int age;
	
	public TestThis(){
		System.out.println("就是这么牛逼");
	}

	public TestThis(String name){
		this();
		this.name=name;
	}
	
	public TestThis(String name,int age){
		this(name);
		this.age=age;
	}
}

public class ThisTest2{
	public static void main(String[] args) {
		
		TestThis tt= new TestThis("刘瘦瘦");//会输出“就是这么牛逼”
	}
}

3.当this作为对象的默认引用使用时,程序可以像访问普通引用变量一样来访问这个this引用,甚至可以把this当成普通方法的返回值。

package com.liuyongbin.thisTest;

public class ReturnThis{
	
	private int age;
	
	public ReturnThis grow(){
		age++;
		return this;//返回调用该方法的对象
	}
	public static void main(String[] args) {
		
		ReturnThis rt=new ReturnThis();
		//可以连续调用一个方法
		rt.grow().grow().grow();
		System.out.println("rt的age Field值是:"+rt.age);//rt的age Field值是:3
		
		System.out.println(rt.grow());//com.liuyongbin.thisTest.ReturnThis@15db9742
		System.out.println(rt);//com.liuyongbin.thisTest.ReturnThis@15db9742
	}
	
}

2.package

package关键字的使用

  1. 为了更好的实现项目中类的管理,提供了包的概念。
  2. 使用package声明类或接口所属的包,声明在源文件的首行。
  3. 包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz 包通常用小写单词标识。通常使用所在公司域名的倒置:com.liushoushou.xxx)、见名知意。
  4. 每"."一次,就代表一层文件目录。
  5. 同一个包下,不能命名同名的接口、类。
  6. 不同的包下,可以命名同名的接口、类。

包的作用:

  1. 包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
  2. 包可以包含类和子包,划分项目层次,便于管理
  3. 解决类名冲突的问题以及控制访问权限

JDK中主要的包介绍

  1. java.lang—包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
  2. java.net—包含执行与网络相关的操作的类或接口。
  3. java.io—包含能提供多种输入/输出功能的类。
  4. java.util—包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
  5. java.text—包含了一些java格式化相关的类。
  6. java.sql—包含了java进行JDBC数据库编程的相关类/接口。

3.import与import static

import的作用

  • 为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类。

import的语法格式

  • import 包名. 类名;

注意

  1. 在源文件中显示的使用import结构导入指定包下的类、接口
  2. 声明在包的声明和类的声明之间
  3. 如果需要导入多个结构,则并列写出即可
  4. 可以使用“xxx.*”的方式,表示可以导入xxx包下的所有结构
  5. 如果使用的类或接口是本包下定义的,则可以省略import结构
  6. 如果使用的类或接口是java.lang包下定义的,则可以省略import结构(Java默认为所有源文件导入java.lang包下的所有类)
  7. 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示
  8. 使用"xxx.*"方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显示导入

import static 的作用

  • 导入指定类或接口中的静态结构:属性或方法

import static 的语法格式

  • 语法格式1:import static 包名.类名.field|methodName;//包层次下指定的类的指定的静态Field、方法
  • 语法格式2:import static 包名.类名.*;//包层次下指定的类的全部静态Field、方法

一句话归纳import和import statiC的作用:使用import可以省略写包名; 而使用import static则可以连类名都省略。

4.super关键字

super关键字的使用

  • 在Java类中使用super来调用父类中的指定操作:
    • super可用于访问父类中定义的属性
    • super可用于调用父类中定义的成员方法
    • super可用于在子类构造器中调用父类的构造器

super调用属性和方法

  1. 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用 父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
  2. 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的 使用"super.属性"的方式,表明调用的是父类中声明的属性。
  3. 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的 使用"super.方法"的方式,表明调用的是父类中被重写的方法。

class BaseClass{
	
	public int a=5;
}
public class SubClass extends BaseClass{
	
	public int a=7;
	public void accessOwner(){
		System.out.println(a);
	}
	
	public void accessBase(){
		//通过super来限定访问从父类继承得到的a field
		System.out.println(super.a);
	}
	
	public static void main(String[] args) {
		
		SubClass sc=new SubClass();
		//输出5
		sc.accessBase();
		//输出7
		sc.accessOwner();
	}
	
	/*
	 * 分析;
	 *	上面程序的BaseClass和SubClass中都定义了名为a的实例Field,则SubClass的a实例Field将会隐藏BaseClass的a实例Field。
	 *当系统创建了subClass对象时,实际上会为SubClass对象分配两块内存,一块用于存储在SubClass类中定义的a Field,
	 *一块用户存储从BaseClass类继承得到的a Field。
	 *	程序中程序访问super.a时,此时使用super限定访问该实例从父类继承得到的a Field,而不是在当前类中定义的a Field。
	 *  如果子类里没有声明和父类同名的Field,那么子类实例方法中访问该Field时,
	 *则无需显示使用super或父类名作为调用者。如果在某个方法中访问名为a的Field,但没有显示指定调用者,则系统查找a的顺序为:
	 *		(1)查找该方法中是否有名为a的局部变量;
	 *		(2)查找当前类中是否包含名为a的Field;
	 *		(3)查找a的直接父类中是否包含名为a的Field,依次上溯a的所有父类,
	 *			直到java.lang.Object类,如果最终不能找到名为a的Field,则系统出现编译错误。
	 *			如果被覆盖的是类Field,在子类的方法中则可以通过父类名作为调用者来访问被覆盖的类Field
	 */
}

注意

当程序创建一个子类对象时,系统不仅会给该类中定义的实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存,即使子类定义了与父类中同名的实例变量。也就是说,当系统创建一个Java对象时,如果该Java类有两个父类(一个直接父类A,一个间接父类B),假设A类中定义了2个实例变量,B类中定义了3个实例变量,当前类中定义2个实例变量,那么这个Java对象将会保存2+3+2个实例变量。如果在子类里定义了与父类中已有变量同名的变量,那么子类中定义的变量会隐藏父类中定义的变。注意不是完全覆盖,因此系统在创建子类对象时,依然会为父类中定义的、被隐藏的变量分配内存空间,所以会出现如下特殊的情形
class Parent{
	public String str="全世界拼成首诗";
}

class Derived extends Parent{
	//定义一个私有的str来隐藏父类的tag实例变量
	private String str="我爱你当做最后一行";
}

public class HideTest{
	
	public static void main(String[] args) {
		
		Derived d = new Derived();
		//程序不访问d的私有变量tag所以下面语句将引起编译错误
		//System.out.println(d.str);
		
		//将d变量显示地向上转型为Parent后,即可访问str实例变量
		System.out.println(((Parent)d).str);//全世界拼成首诗
	}
}

super调用父类构造器

  • 子类不会获得父类的构造器,但子类构造器里可以调用父类构造器的初始化代码,类似于前面所介绍的一个构造器调用另一个构造器。

子类调用父类构造器的几种情况

  • 子类构造器执行体的第一行使用super显示调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器。
  • 子类构造器执行体的第一行代码使用this显示调用本类中重载的构造器,系统将根据this调用里传入的实参表调用本类中的另一个构造器。执行本类中另一个构造器时会调用父类构造器。
  • 子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。

不管上面哪种情况,当调用子类构造器来初始化对象时,父类构造器总会在子类构造器之前执行:不仅如此,执行父类构造器时,系统会再次上溯执行其父类构造器…以此类推,创建任何Java对象,最先执行的总是Java.lang.Object类的构造器。
对于下图所示的继承树:如果创建ClassB的对象,系统将先执行java.lang.Object类的构造器,在执行ClassA类的构造器,然后才执行ClassB类的构造器,这个执行过程还是最基本的情况。如果ClassB显示调用ClassA的构造器,而该构造器又调用了ClassA类中重载的构造器,则会看的ClassA两个构造器先后执行的情形(看下面的代码演示)。
在这里插入图片描述

/*
 * 测试继承树
 */

class Creature{
	
	public Creature(){
		//super();调用object的空构造器
		System.out.println("Creature无参的构造器");
	}
}

class Animal extends Creature{
	
	public Animal(String name){
		//super();//调用Creature的空构造器
		System.out.println("Animal带一个参数的构造器,"+"该动物的name为"+name);
	}
	
	public Animal(String name,int age){
		//使用this调用一个重载的构造器
		this(name);
		System.out.println("Animal带两个参数的构造器,"+"其age为"+age);
	}
}
public class Wolf extends Animal {
	
	public Wolf(){
		//显示调用父类有两个参数的构造器
		super("灰太狼",3);
		System.out.println("Wolf无参数的构造器");
	}
	public static void main(String[] args) {
		new Wolf();
		//输出结果
		/*
		 *  Creature无参的构造器
			Animal带一个参数的构造器,该动物的name为灰太狼
			Animal带两个参数的构造器,其age为3
			Wolf无参数的构造器

		 */
	}
}

从上面运行程序来看,创建任何对象总是从该类所在继承数最顶层类的构造器开始执行,然后依次向下执行,最后才执行本类构造器。如果某个父类通过this调用了同类中重载的构造器,就会依次执行此父类的多个构造器。

为什么我们创建Java对象时从未感觉到java.lang.Object类的构造器被调用过?

答:因为自定义的类从未显式调用java.lang.Object类的构造器,即使显式调用,java.lang.Object类也只有一个默认的构造器可被调用。当系统执行java.lang.Object类的默认构造器时,该构造器的执行体并未输出任何内容,所以你感觉不到调用过java.lang.Object类的构造器。

this和super的区别
在这里插入图片描述

  • 在一个构造器中调用另一个重载的构造器使用this调用来完成,在子类构造器中调用父类构造器使用super调用来完成
  • 使用super调用父类构造器和使用this调用同一个类的重载的构造器都必须在构造器的首行(也就说明this和super不会同时出现
  • 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)否则会造成死循环。这也就说明,在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器。(子类中所有的构造函数默认都会访问父类中的空参数的构造函数。当然,如果子类中指定了访问父类带参数的构造函数,就不会访问父类默认的构造函数)

5.static关键字

去看static

6.abstract关键字

abstract关键字

7.final关键字

final关键字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值