package
1. 说明
- package:包
- package用于指明该文件中定义的类、接口等结构所在的包
一个源文件只能有一个声明包的package语句
package语句作为Java源文件的第一条语句出现。
若缺省该语句,则指定为无名包。- 包名,属于标识符,满足标识符命名的规则和规范(
全部小写
)、见名知意- 包通常使用所在公司域名的倒置:com.atguigu.xxx。
- 大家取包名时不要使用"java.xx"包
- 包对应于文件系统的目录,package语句中用 “.” 来指明包 / 目录的层次,每一个 . 就表示一层文件目录。
- 同一个包下可以声明多个类或接口,但是
不能定义同名的类或接口, 同一个包下,类和接口也不允许同名。
- 不同的包下可以定义同名的结构(类、接口)
2. 包的作用
- 包可以包含类和子包,划分
项目层次
,便于管理 - 帮助
管理大型软件
系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式 - 解决
类命名冲突
的问题 - 控制
访问权限
3. JDK中主要的包
java.lang
----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
java.net
----包含执行与网络相关的操作的类和接口。
java.io
----包含能提供多种输入/输出功能的类。
java.util
----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
java.text
----包含了一些java格式化相关的类
java.sql
----包含了java进行JDBC数据库编程的相关类/接口
java.awt
----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
import
- import : 导入
- import语句来显式引入指定包下所需要的类。相当于
import语句告诉编译器到哪里去寻找这个类
。 - import语句,声明在
包的声明和类的声明之间。
- 如果需要导入多个类或接口,那么就并列显式多个import语句即可
- 如果使用
a.*
导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。 - 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
- 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。
- 如果在代码中使用不同包下的同名的类,那么就需要使用类的
全类名
的方式指明调用的是哪个类。 - (了解)
import static
组合使用时:调用指定类或接口下的静态的属性或方法
final
-
final的理解:最终的
-
final可以用来修饰的结构:类、方法、变量
-
具体说明:
-
final修饰类:
表示此类不能被继承。
比如:String、StringBuffer、StringBuilder类 -
final修饰方法:
表示此方法不能被 (子类) 重写。
比如:Object类中的getClass() -
final修饰变量:既可以修饰成员变量,也可以修饰局部变量。
此时的"变量"其实就变成了"常量",意味着一旦赋值就不可更改。
- final修饰成员变量: 有哪些位置可以给成员变量赋值?
显式赋值
代码块中赋值
构造器中赋值- final修饰局部变量:
方法内声明的局部变量:在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改
方法的形参:在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改
-
-
final与static搭配:修饰成员变量时,此成员变量称为:全局常量。
- 全局指的是static修饰,整个内存空间中只有一份
- 常量是final修饰,值不可更改
- Math的PI
代码展示:final修饰成员变量,如何赋值
class E{
//成员变量
final int MIN_SCORE = 0; //显示赋值
final int MAX_SCORE;
final int LEFT;
{
MAX_SCORE = 100; //代码块中赋值
}
//构造器赋值
public E(){
LEFT = 2;
}
public E(int left){
LEFT = left;
}
}
static
-
static: 静态的
-
static 用来修饰的结构:属性、方法; 代码块、内部类;
-
static修饰属性
复习:变量的分类方式1:按照数据类型:基本数据类型、引用数据类型
方式2:按照类中声明的位置:
成员变量:
- 使用static修饰的成员变量:静态变量、类变量
- 不使用static修饰的成员变量:非静态变量、实例变量
局部变量:
方法内、方法形参、构造器内、构造器形参、代码块内等。
静态变量:类中的属性使用static进行修饰。
对比静态变量与实例变量:
① 个数
静态变量:在内存空间中只有一份,被类的多个对象所共享。
实例变量:类的每一个实例(或对象)都保存着一份实例变量。
② 内存位置
静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
实例变量:存放在堆空间的对象实体中。
③ 加载时机
静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份
。
实例变量:随着对象的创建而加载。每个对象拥有一份实例变量。
④ 调用者
静态变量:可以被类
直接调用,也可以使用对象
调用。
实例变量:只能使用对象
进行调用。
⑤ 判断是否可以调用 —> 从生命周期的角度解释
类变量 实例变量
类 yes no
对象 yes yes
⑥ 消亡时机
静态变量:随着类的卸载而消亡
实例变量:随着对象的消亡而消亡
static修饰方法:(类方法、静态方法)
-
随着类的加载而加载
-
可以通过“类.静态方法”的方式,直接调用静态方法
-
调用者
类方法 实例方法 类 yes no 对象 yes yes
-
☆☆☆ 静态方法内可以调用静态结构(属性、方法)(前缀是当前类的类名,可以省略)。不可以调用非静态的结构(属性、方法)。
-
☆☆☆ 在类的非静态方法中,可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)
-
☆☆☆ static修饰的方法内,不能使用this和super
- 因为this指的是当前对象,super指的是当前对象的父对象;而static修饰时,调用者可能是对象也可能是类,是类的话,就不存在对象一说了。
静态方法内不可以调用非静态的结构:
静态方法和静态变量是类一加载的时候就加载了,此时还没有创建对象,实例变量或实例方法无法加载,所以静态方法内不可以调用非静态的结构。
开发中
什么时候需要将属性声明为静态的?
判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的。
开发中,常将一些常量声明是静态的。比如:Math类中的PI
什么时候需要将方法声明为静态的?
方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法
开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类
this
-
this可以调用的结构:成员变量、方法、构造器
-
this的理解:
当前对象(在方法中调用时) 或 当前正在创建的对象(在构造器中调用时)
-this在非static修饰的方法中调用成员变量、方法
class User{
String name;
int age;
//此方法内的this不可省略,因为形参名与成员变量名重名了。this.name表示属性(成员变量),name表示形参(局部变量)
public User(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){ //此方法内的this一般是省略的
System.out.println(this.name + "吃饭");
this.sleep();
}
//此方法内的this不可省略,this.name表示属性,name表示形参
public void sleep(String name){
System.out.println(this.name + "和他的狗"+ name + "一起睡觉");
}
}
-
this调用构造器
- 格式:this(形参列表)
- 我们可以在类的构造器中,使用this(…)调用当前类中指定的其它构造器
- 要求:“this(形参列表)” 必须声明在当前构造器的
首行
- 结论:“this(形参列表)” 在构造器中最多声明
一个
- 如果一个类中声明了n个构造器,则最多有n-1个构造器可以声明有 “this(形参列表)” 的结构
super
-
为什么需要super?
-
子类继承父类以后,使用super调用父类中被重写的方法
-
子类继承父类以后,使用super调用父类中被覆盖的同名属性
-
-
super的理解:父类的
-
super可以调用的结构:属性、方法、构造器
- super调用属性、方法
子类继承父类以后,我们可以在子类的方法或构造器中,调用父类中声明的属性或方法(满足封装性的前提下)。
一般情况下,我们可以考虑省略"super."的结构
。
如果出现子类重写了父类的方法或子父类中出现了同名的属性时,则必须使用"super."的声明,显式的调用父类被重写的方法或父类中声明的同名的属性。
-
-
super调用构造器
① 子类继承父类时,不会继承父类的构造器。只能通过“super(形参列表)”的方式调用父类指定的构造器。② 规定:“super(形参列表)”,必须声明在构造器的
首行
。③ 我们前面讲过,在构造器的首行可以使用"this(形参列表)",调用本类中重载的构造器,
结合②,结论:在构造器的首行,“this(形参列表)” 和 "super(形参列表)"只能二选一。④ 如果在子类构造器的首行既没有显示调用"this(形参列表)“,也没有显式调用"super(形参列表)”,则子类此构造器
默认调用"super()"
,即调用父类中空参的构造器。⑤ 由③和④得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器。
⑥ 由⑤得到:一个类中声明有n个构造器,最多有n-1个构造器中使用了"this(形参列表)“, 则剩下的那个一定使用"super(形参列表)”。
⑦综上,在创建子类对象的过程中,一定会调用父类中的构造器
-
我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接的调用到父类的构造器。
也正因为调用过父类的构造器,我们才会将父类中声明的属性或方法加载到内存中,供子类对象使用。
子类对象实例化全过程 ☆☆☆
-
从结果的角度来看:体现为类的继承性
当我们创建子类对象后,子类对象就获取了其父类中声明的所有的属性和方法
,在权限允许的情况下,可以直接调用 (比如private修饰的就不可以调用)。 -
从过程的角度来看:
当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接的调用到其父类的构造器,而其父类的构造器同样会直接或间接的调用到其父类的父类的构造器,…,直到调用了Object类中的构造器为止。
正因为我们调用过子类所有的父类的构造器,所以我们就会将父类中声明的属性、方法加载到内存中,供子类的对象使用
。 -
问题:创建子类的对象时,内存中到底有几个对象?
就只有一个对象!即为当前new后面构造器对应的类的对象。
面试题:☆☆☆
* 这里面一共有三个info,三个getInfo、setInfo
* f有一个;s有两个,一个父类的,一个继承自父类的
*/
public class Test {
public static void main(String[] args) {
Father f = new Father();
Son s = new Son();
System.out.println(f.getInfo());//atguigu
System.out.println(s.getInfo()); //atguigu
s.test();//atguigu atguigu
System.out.println("-----------------");
s.setInfo("大硅谷");
System.out.println(f.getInfo());//atguigu
System.out.println(s.getInfo());//大硅谷
s.test(); //大硅谷 大硅谷
}
}
class Father{
private String info = "atguigu";
public void setInfo(String info){
this.info = info;
}
public String getInfo(){
return info;
}
}
class Son extends Father{
private String info = "尚硅谷";
public void test(){
System.out.println(this.getInfo());
System.out.println(super.getInfo());
}
// public String getInfo(){
// return info;
// }
// 如果son有getInfo()时的结果
// atguigu
// 尚硅谷
// 尚硅谷
// atguigu
//-----------------
// atguigu
// 尚硅谷
// 尚硅谷
// 大硅谷
}
public class Test {
public static void main(String[] args) {
new A(new B());//b a ab
}
}
class A {
public A() {
System.out.println("A");
}
public A(B b) {
this();
System.out.println("AB");
}
}
class B {
public B() {
System.out.println("B");
}
}
public class Test {
public static void main(String[] args) {
new A(new B());//a b a ab
}
}
class A {
public A() {
System.out.println("A");
}
public A(B b) {
this();
System.out.println("AB");
}
}
class B extends A{
public B() {
System.out.println("B");
}
}
代码块
-
代码块(或初始化块)的作用:
用来初始化类或对象的信息
(即初始化类或对象的成员变量) -
代码块的修饰:
只能使用static进行修饰。
-
代码块的分类:
静态代码块:使用static修饰
非静态代码块:没有使用static修饰 -
具体使用:
4.1 静态代码块:- 随着类的加载而执行
- 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次
- 作用:用来初始化类的信息
- 内部可以声明变量、调用属性或方法、编写输出语句等操作。
- 静态代码块的执行要先于非静态代码块的执行
- 如果声明有多个静态代码块,则按照声明的先后顺序执行
- 静态代码块内部只能调用静态的结构(即静态的属性、方法),
不能调用非静态的结构(即非静态的属性、方法)
4.2 非静态代码块:
- 随着对象的创建而执行
- 每创建当前类的一个实例,就会执行一次非静态代码块
- 作用:用来初始化对象的信息
- 内部可以声明变量、调用属性或方法、编写输出语句等操作。
- 如果声明有多个非静态代码块,则按照声明的先后顺序执行
- 非静态代码块内部可以调用静态的结构(即静态的属性、方法),
也可以调用非静态的结构(即非静态的属性、方法)
内部类
-
什么是内部类?
将一个类A定义在另一个类B里面,类A就称为内部类(InnerClass)
,类B则称为外部类(OuterClass)。 -
为什么需要内部类?
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。→ 遵循
高内聚、低耦合
的面向对象开发原则。 -
内部类使用举例:
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的key和value -
内部类的分类:(参考变量的分类)
-
成员内部类:直接声明在外部类的里面。
使用static修饰的:静态的成员内部类
不使用static修饰的:非静态的成员内部类 -
局部内部类:声明在方法内、构造器内、代码块内的内部类
匿名的局部内部类
非匿名的局部内部类
-
-
成员内部类
-
关于成员内部类的理解:
从类的角度看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
- 可以使用public和缺省修饰从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
-除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
- 可以使用static修饰 -
如何创建成员内部类的实例
public class Test {
public static void main(String[] args) {
//1. 创建Person的静态的成员内部类的实例
Person.Dog dog = new Person.Dog();
dog.eat();
//2. 创建Person的非静态的成员内部类的实例
// Person.Bird bird = new Person.Bird(); //报错
Person p1 = new Person();
Person.Bird bird = p1.new Bird();//正确的
bird.eat();
}
}
class Person{ //外部类
String name = "Tom";
int age = 1;
//静态的成员内部类
static class Dog{
public void eat(){
System.out.println("狗吃骨头");
}
}
//非静态的成员内部类
class Bird{
String name = "啄木鸟";
public void eat(){
System.out.println("鸟吃虫子");
}
}
}
- 如何在成员内部类中调用外部类的结构
class Person{ //外部类
String name = "Tom";
int age = 1;
public void eat(){
System.out.println("人吃饭");
}
//非静态的成员内部类
class Bird{
String name = "啄木鸟";
public void eat(){
System.out.println("鸟吃虫子");
}
public void show(String name){ //调用时形参传入“黄鹂”
System.out.println("age = " + age); //省略了Person.this age = 1
System.out.println("name = " + name); //黄鹂
System.out.println("name = " + this.name); //啄木鸟
System.out.println("name = " + Person.this.name); //Tom
}
public void show1(){
eat(); //鸟吃虫子
this.eat(); //鸟吃虫子
Person.this.eat(); //人吃饭
}
}
//静态的成员内部类
static class Dog{
public void eat(){
System.out.println("狗吃骨头");
}
}
}
- 局部内部类的基本使用
- 接口的实现类的对象
- 接口的实现类的匿名对象
- 接口的匿名实现类的对象
- 接口的匿名实现类的匿名对象
public class Test { //一下方式四选一,同时出现在方法中编译报错
//开发中的场景
public Comparable getInstance(){
//提供了实现了Comparable接口的类
//方式1:提供了接口的实现类的对象
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
MyComparable m = new MyComparable();
return m;
//方式2:提供了接口的实现类的匿名对象
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new MyComparable();
//方式3:提供了接口的匿名实现类的对象
Comparable c = new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
return c;
//方式4:提供了接口的匿名实现类的匿名对象
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
-
局部内部类的使用扩展:
抽象类:
继承于抽象类的子类的对象
继承于抽象类的匿名子类的对象
继承于抽象类的子类的匿名对象
继承于抽象类的匿名子类的匿名对象普通类C:
继承于类C的子类的对象
继承于类C的匿名子类的对象
继承于类C的子类的匿名对象
继承于类C的匿名子类的匿名对象
class Test{
public void A(){
C c = new C();
c.method2(); //C
//举例7:提供了一个继承于C的匿名子类的对象
C c1 = new C(){};
c1.method2(); //C
//提供了一个继承于c的匿名子类的对象,并重写了父类的method2方法
C c2 = new C(){
public void method2(){
System.out.println("SubC");
}
};
c2.method2(); //SubC
}
}
class C{
public void method2(){
System.out.println("C");
}
}
字节码文件中的类名: