JAVA第九天

Advanced Language Features ... 高级语言特性
一.static 修饰符
静态的.被这个static修饰的东西,就是静态的,那么就是属于类的(最好是用 类名.静态资源 的形式来访问,eg: Prople.age; Prople.show();),在类创建的时候就会被创建。
eg:private int age; 叫 成员变量
private static int age; 叫 静态成员变量
注意:静态的东西是属于类-->导致了 在静态方法中不能使用this关键字
非静态的东西是属于对象
1.1:用在什么地方。
1.1.1:修饰成员变量: private static int age;
1.1.2:修饰成员方法: public static void show(){}
1.1.3:修饰代码块 : static{}

1.2:静态代码块/非静态代码块(匿名代码块)
1.2.0:匿名代码块:在创建对象的时候会执行.
从上往下依次执行
每创建一个对象都会执行一次。
1.2.1:静态代码块:在类加载的时候会调用一次,且只调用一次。
用途:一般用来给类中的【静态属性赋值】。
1.2.2:非静态代码块:每次创建对象的时候会执行。

二:各种静态资源
2.1:静态变量:修饰符
2.1.1:声明:和声明成员变量类似:
public static int age;

2.1.2:内存机制:
静态资源都是[属于类],
在内存中存放于静态[方法区],
类加载(eg:java Hello jvm就会加载 Hello 类)的时候就会创建静态资源的内存空间。
所以,静态资源[一个类只有一份],
那么这个类的任何实例对象去操作该类的静态资源,实质上是操作同一个资源。
-->[多个实例操作静态资源,
是操作同一个资源]。

结论:static变量在内存中只有一份,被类的所有实例对象共享。
注意:jvm在类加载的时候为static变量分配内存。
实例变量是类加载完成以后,创建对象的时候才分配内存。

2.1.3:内存机制测试:
测试:
Prople 类中有一个静态属性 int age;
利用Prople创建3个实例对象
分别用这3个实例对象操作Prople中静态的age操作(age++),
每次操作完成,分别打印 查看age的值
代码:
class Prople{
public static int age;
}
class T{
public static void main(String msg[]){
Prople p1 =new Prople();
Prople p2 =new Prople();
Prople p3 =new Prople();
p1.age++;
System.out.println(p1.age);
p2.age++;
System.out.println(p2.age);
p3.age++;
System.out.println(p3.age);
}
}
2.2:静态方法:成员方法的一种(回顾:成员方法 = 静态方法 + 实例方法)
2.2.1:声明:和声明方法类似,但是需要加static关键字(eg:public staitc void show(){})
2.2.2:内存机制:
1,静态方法也是在类加载的时候分配方法区内存空间。--->在静态方法中不能使用this关键字-->想想为什么?
2,静态方法中不能使用super关键字:因为super关键字用来访问当前实例从父类中继承的方法和属性。super关键字与类的特定实例相关;

2.2.3:静态方法特性:
1,静态方法必须被实现:静态方法用来表示某个类所特有的功能,这种功能的实现不依赖于类的具体实例,也不依赖它的子类。

2,静态方法不存在重写这一说
3,父类的静态方法不能被覆盖为非静态方法
4,父类的非静态方法不能被子类覆盖为静态方法
【子父类中不能有同名方法 其中一个被static修饰】

5,静态方法在内存中只有一份,
无论该类有多少个实例,都共用一个内存。

6.静态方法可以直接使用,利用全类名调用
类名.方法名();
全包名.类名.方法名();
7.静态方法只允许访问静态成员。

8.静态方法中不能使用this(因为this是与实例相关的)。

2.3:静态代码块:类中的静态代码块,它不存在于任何方法。
1, 在java虚拟机中类加载会执行这些静态代码块。如果类中有多个静态代码块,会从上到下依次执行。
2, 静态代码块只能定义在类里面,不能定义在方法里面。
3, 静态代码块里的变量都是局部变量,只在当前代码块内有效。
4, 静态代码块只能访问类的静态成员,而不允许访问实例成员。
5, 静态代码块用来给静态变量赋初始值。
6, 静态代码块主要用于类的初始化。它只执行一次,并在main函数之前执行。
class Student{
public static String name = show();
private static int age;
main(){

}
public Student(){
age = 20;
}
static{
2
age = 10;
}
public static String show(){
3
return "tom";
}
}


2.4:类执行的内存机制(讲过)
2.4.1:jvm中有什么内存分别存放什么
2.4.1.1:虚拟机栈(栈内存):八大基本数据类型+对象引用+returnAddress
特点:1,方法执行需要在栈里执行,就是方法要执行 需要先入栈。栈中的方法叫 栈帧。-->每一个方法被执行都会在当前栈中创建一个栈帧。
2,每个线程都对应有一个栈。
3,默认分配内存1M。
栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和 对象句柄。
int a = 3; 这些称为自动变量,自动变量存的是字面值

2.4.1.2:堆:对象实例(所有的对象实例以及数组都要在堆上分配)
2.4.1.3:方法区
2.4.1.3.1:方法区:存储已被虚拟机加载的类信息,字段信息,方法信息,静态变量,类classloader的引用,类Class的引用,即时编译器编译后的代码等数据。
2.4.1.3.2:运行时常量池:常量,编译期生成的各种字符面量和符号引用,着部分内容将在类加载后存放在常量池中。
注:在方法区中,每个类型都对应一个常量池,存放该类型所用到的所有常量。
这里暂且只认为 常量池 = final修饰的变量 + ""String的值。
2.4.1.4:本地方法栈: 为虚拟机使用到的native方法服务.
2.4.1.5:程序计数器:当前线程所执行字节码的行号指示器。
2.5: 代码整体在内存的执行
eg:
public class F {

public static int age=12;

public String name="小明";

{
System.out.println("F\t匿名代码块");
}
static{
System.out.println("F\t静态代码块");
}
public static int show(){
System.out.println("F\t静态方法show");
return 10;
}
public void say(){
int a = 1;
String c = new String("abc");
System.out.println("F\t普通方法");
}
public F() {
System.out.println("F\t构造器");
}
public static void main(String[] args) {
F f = null;
f = new F();
f.say();
}
}

1,java F 1.1,类加载,通过F类的 全类名(包名+类名) 获得二进制字节流, 将字节流保存到 方法区 并且在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口
1.2,准备阶段:在方法区为类变量(static变量)分配内存,
注: 实例变量将会在对象实例化时随着对象一起分配在java堆中
2,public static int age=12 2.1:准备阶段:在方法区开辟int所需的空间赋值为0。
2.2:初始化:age = 12;(初始化当前类会先初始化其父类-->父类的static资源会优先被加载)
2.3:初始化:当前类中的static代码块 static方法加载到方法区内存。
2.4:类加载完成。开始执行main方法
2.5:main方法进栈

3,F f = null; 3.1:在main的栈帧中声明变量F f。

4,f = new F(); 4.1:首先在堆中创建F类的实例对象,
4.2:在堆中初始化成员变量
4.3:在堆中初始化成员方法
4.4:将该实例对象的引用赋值给f

5,f.say() 5.1:通过f所指向的内存地址,找到在堆中的say()方法.
5.2:调用say()方法,say()方法进栈。

6,int a = 1; 6.1:声明变量a:在栈中开辟内存空间声明a, 1 是一个基础数据类型(也是保持在栈中)。
7,String c =
new String("abc"); 7.1:声明String c 在栈中开辟内存声明 c, new String("abc") 是引用数据类型 在堆中创建String实例,由于String的值比较特殊.所以会先在常量池中寻找是否有abc 如果有直接引用,没有就在常量池创建abc然后赋值给堆中的对象。
8,执行system
9, say()方法执行完成,弹栈(出栈)
10,main方法执行完成,弹栈(出栈),代码执行结束


2.6: new一个对象的时候JVM都做了那些事情:
1.之前没有进行类加载
1.类加载,同时初始化类中静态的属性(赋默认值)
2.执行静态代码块
3.分配内存空间,同时初始化非静态的属性(赋默认值)
4.如果声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
5.执行匿名代码块
6.执行构造器
7.返回内存地址
静态 > 非静态 > 构造器
Class F{
public static String name = show();
static{
syso("ffff")
}
{
syso("nnnffff")
}
public F(){
syso("ffffggg")
}
public static String show(){
Syso("f show");
return "tom"
}
}
Class Z extends F{
public static String name = show();
static{
syso("ffff")
}
{
syso("nnnffff")
}
public Z(){
syso("ffffggg")
}
public static String show(){
Syso("f show");
return "tom"
}
}

2.之前已经进行了类加载
1.分配内存空间,同时初始化非静态的属性(赋默认值)
2.如果声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
3.执行匿名代码块
4.执行构造器
5.返回内存地址



2.7:静态导入
2.7.1:since: jdk5.0
2.7.2:使用:import static java.lang.Math.random;
import static 包.静态方法名/静态属性名;
2.7.3:作用:在当前类中就可以使用导入的静态属性或静态方法(直接方法名使用,不用类名.方法名调用);

三: final修饰符
3.1:final意义: 具有"不可改变的"含义,它可以修饰非抽象类、非抽象成员方法和变量。

3.1.1: final修饰类 :该类不能被继承,没有子类.
final class Student{}
final class String{
}

3.1.2: final修饰方法: 该方法不能被覆盖.
public final void show(){}
Object: getClass();

3.1.3: final修饰变量:该变量是常量(常量池),【只能被赋一次值】。
private int AGE = 10;

* 3.1.4: private类型的方法默认是final类型的---> final不能用来修饰构造方法,因为"方法覆盖"这一概念仅适用于类的成员方法,而不适用于类的构造方法,父类的构造方法和子类的构造方法之间不存在覆盖关系. 因此用final修饰构造方法是无意义的。父类中用private修饰的方法不能被子类的方法覆盖.

3.2:final类
回顾:继承关系是打破封装,子类能够访问父类的方法,而且能以方法覆盖的方式修改实现细节。
3.2.1:子类有可能会错误地修改父类的实现细节;
3.2.2:出于安全,类的实现细节不允许有任何改动;
3.2.3:在创建对象模型时,确信这个类不会再被扩展;
问题:String类能不能被继承?

3.3:final方法
3.3.1: 某些情况下,出于安全原因,父类不允许子类覆盖某个方法,此时可以把这个方法声明为final类型。
3.3.2: final方法不能被重写(可以重载多个final修饰的方法)。
eg:Object类中的getClass()方法是final类型的--->调用任何类对象的getClass()方法就可以看到该对象的原始面目。

3.4:final变量
【3.4.1:final修饰非静态成员变量,必须要赋值,并且只能在下面三个地方赋值,赋值以后不能改。
3.4.1.1:声明的同时 赋值
3.4.1.2:匿名代码块内 赋值
3.4.1.3:类中任何构造器 赋值
3.4.2:final修饰static静态的成员变量
3.4.2.1:声明的同时 赋值
3.4.2.2:static代码块内 赋值
final修饰类不能被继承
final修饰方法不能被重写】

3.4.3:final修饰变量 结论
3.4.3.1:final可以修饰任何变量.
3.4.3.1:final修饰的变量必须赋初始值
3.4.3.3:final修饰的变量被一次赋值以后不能跟改

四:abstract修饰符
4.1:定义:abstract 抽象的(不完整的),可以用来修饰类和成员方法.

4.2:abstract修饰类:抽象类。public abstract class A{}
4.2.1:该类不能被实例化(不能创建对象)。但是有构造器。
4.2.2:该类可以有抽象方法,也可以没有。可以有非抽象方法。

4.2.3:抽象类中可以没有抽象方法,
但包含了抽象方法的类必须被定义为抽象类。
4.2.4:没有抽象构造方法,也没有抽象静态方法;
4.2.5:抽象类中可以有成员变量,成员方法......

问题1:抽象类是否能被final修饰?

4.3:abstract修饰方法:抽象方法。
4.3.1:没有方法体。用来描述功能,但是没有实现。
4.3.2:被abstract 修饰的普通方法 就是抽象方法
抽象方法一定不能有代码体
public abstract void show();
4.3.3:包含抽象方法的类 一定是抽象类
4.3.4:抽象类生来就是被继承的,
子类中需要重写父类中的抽象方法,
子类中如果不想重写父类中的抽象方法,
那么子类中的该方法也要声明为抽象方法,
子类也要声明为抽象类。

五: 接口(接口类型)
5.1:接口是为了解决了java中多继承的问题。
eg:
5.2:抽象类抽象到极致就是接口。接口是抽象类的另外一种形式。
5.3:什么时候用接口什么时候用抽象类
5.3.1: 接口侧重于方法,如果抽象出来没有属性就用接口。
5.3.2: 抽象类侧重于属性,抽象出来有属性就可以使用抽象类
5.3.3: 在面向对象了理解抽象的时候:可以使用抽象类的地方就可以使用接口,使用接口的地方也可以使用抽象类。
5.4:接口的定义:使用interface关键字定义接口
eg: public interface A{} 这个A就是接口。
5.5:接口中可以写什么
5.5.1:成员变量:成员变量默认类型是 public static final类型 必须被显式初始化

5.5.2:成员方法:
5.5.2.1:不能有方法体.eg:public void show();
5.5.2.1:可以没有权限修饰符,默认是 public abstract
问题:在接口中是否可以使用final修饰方法?

5.5.3:不能有任何形式的代码块
5.5.4:接口没有构造方法,不能被实例化-->不能 new 接口名();
5.6:接口的多继承
5.6.1:一个类智能继承一个父类,但是可以实现多个接口,用逗号隔开。
eg:class Student implements A,B{}
一个类实现了某[多]个接口,就要重新接口中没有实现的方法,不然编译报错。
5.6.2:一个接口可以继承多个接口
eg:interface C extends A,B{}
结论:java中类没有多继承,接口可以多继承。
5.7:接口的用途
5.7.1:定义规范.
5.7.2:扩展功能. java类与类之间只能单继承,类与接口之间可以多实现比如像软件升级不可能改源码但是可以定义多个接口增强功能,以后复写接口中的抽象方法。
* 5.7.3:面向接口编程:
优点:接口和实现分离了,适于团队的协作开发。主要为了实现松散耦合的系统,便于以后升级,扩展。
缺点: 设计难,在没有写实现的时候,就得想好接口,接口一变,全部乱套,这就是所谓的设计比实现难。
5.7.4:接口一般配合多态使用 接口A a= new 接口实现类B();
5.7.5:接口命名一般 以 I 开头,接口的实现类,在接口的同级目录下创建impl包,用来存放所有改包下的接口,命名 加 Impl 后缀
eg: 接口 IBookDao
实现类 impl/BookDaoImpl
5.8:接口和抽象类对比
1. 相同点:
a. 都不能被实例化;
b. 都能包含抽象方法;
2. 不同点;
a. 抽象类中可以为部分方法提供默认的实现,从而避免子类中重复实现它们,提高代码的可重用性,而接口中只能包含抽象方法;
b. 一个类只能继承一个直接的父类,这个父类有可能是抽象类;但一个类可以实现多个接口,这是接口的优势所在。

猜你喜欢

转载自www.cnblogs.com/zjw19971001/p/11305057.html