文章目录
1. static
1.1 概述
- 在java中,是一个关键字,
static
表示"全局"或者"静态"的意思。 - 可以通过
static
实现一个全局概念,static
修饰的资源称为静态资源。- 用来修饰 成员变量和成员方法,当然也可以 修饰代码块。
1.2 特点
- 可以修饰成员变量、成员方法。
- 优先于对象存在。即:静态是先存在,对象是后存在。
- 静态资源可以用 “ 类名 或者 对象 ”,进行调用,而非静态只能用对象。
- 全局 只有一份,在全局共享。
- 静态区域内,不允许使用
this和super关键字
。(参考第二点?那时可能还没有对象)
1.2.1 static 入门案例
- 注意, IDEA对象引用调用静态资源,没有提示需要自己写全!
public class StaticDemo {
public static void main(String[] args) {
Students.speak(); // 类名直接调用;
// 创建对象 s1 和 s2
Students s1 = new Students();
Students s2 = new Students();
// 对象调用静态资源,idea没有提示需要自己补全 country!
System.out.println(s1.country);
/*重点:
静态资源在内存中只有一份,被全局对象共享
所以,不管怎么修改,查看结果都是我们刚才修改的值。
* */
// 正常对象调用。
s1.country="zhongguo";
System.out.println(Students.country);
System.out.println(s2.country);
// 类名直接调用。
Students.country="中国";
System.out.println(s1.country);
System.out.println(s2.country); // s2输出结果也是修改后的值
// 修改普通成员变量的值
s1.name="王一博";
System.out.println(s1.name); // 王一搏
System.out.println(s2.name); // null
}
}
class Students{
String name; //姓名
// 静态修饰成员变量
static String country="CHINA"; // 国籍
//静态修饰 方法
public static void speak(){
System.out.println("中国话!");
}
//普通方法
public void eat(){
System.out.println("吃饺子!");
}
}
输出结果:
中国话!
CHINA
zhongguo
zhongguo
中国
中国
王一搏
null
1.3 简单介绍:静态方法内存图
- 只需要简单理解即可。
- 内存划分简单理解为三个区域,
栈区域,堆区域,方法区区域(普通方法区,静态方法区)。
- 当类加载的时候,静态资源和main都在静态方法区,普通方法在普通方法区,这两个方法区的资源是共享的,非静态区的资源只能被对象调用,静态区有类名去调用,也可以用对象调用。加载完方法之后会生成该类方法区的标识即地址值 0x0012,
- Person.speak();相当于类加载了,开始开辟空间,而非静态需要创建完对象才开辟空间。
- 栈中的p就是保存了堆内存的地址空间,通过地址值去依次查找,如方法,如变量。(当对象创建完成之后,会生成地址值)
1.4 静态和非静态调用关系
- 静态只能调用静态资源,非静态可以访问静态和非静态。
public class Students {
//定义普通属性
String name;
//定义静态资源
static int age;
public void study(){
System.out.println("学习ing... "+age);
System.out.println(age); //普通方法可以调用静态资源!
}
public static void think(){
System.out.println("正在思考问题... ");
//是否可以调用普通方法 !??
// study(); // 无法调用
}
public static void love(){
System.out.println("想谈恋爱... ");
// System.out.println(name); 不可以调用 非静态资源
think(); // 可以调用静态方法 think
}
}
class Demo2{
public static void main(String[] args) {
//TODO 测试 ...为什么静态只能调用静态?
}
}
- 怎么理解这句话:静态只能调用静态资源,非静态可以访问静态和非静态。
2. 代码块
2.1 语法结构
- 啥是代码块?
在java中用{}括起来的称为代码块
2.1.1 静态代码块
- 用
static{}
包裹起来的代码片段,随着类的加载而加载,并且只会被加载一次。- 作用:一般用于项目初始化。
2.1.2 构造代码块
- 构造代码块是在
类中方法外用 {} 括起来的代码
,抽取出来。在调用构造方法的时候会去自动调用构造代码块。- 作用:主要是用于提取所有构造方法中的共性功能, 构造代码快优先于构造方法。
2.1.3 局部代码块
- 是在
方法名后面用 {} 括起来的代码段
。- 作用:是限定变量的生命周期和提高效率。
2.2 代码块测试
- 思考? 那么执行顺序是怎么样的?
public class Animal {
static int age;
//类的无参构造
public Animal(){
System.out.println("我是无参构造方法...");
}
// 构造代码块优先于 构造方法执行
/*
构造代码块 >构造方法。
*/
{
age=20; // 可以用构造代码块进行初始化
System.out.println("我是构造代码块!");
}
static{
age=30; // 优先加载 会先初始化 age =30;
System.out.println("我是静态代码块");
}
public void sing(){
String name; //局部代码块可以控制局部属性;
System.out.println("日本");
System.out.println("乌克兰");
{
name = "美国";
System.out.println(name);
}
}
}
class Demo3{
public static void main(String[] args) {
Animal a = new Animal();
a.sing();
System.out.println(a.age); // 先为 30,在为20 (后静态加载)
}
}
-
输出结果:
我是静态代码块 我是构造代码块! 我是无参构造方法... 日本 乌克兰 美国 20
- 执行顺序
静态代码块 > 构造代码块 > 构造方法 > 局部代码块
。
3. final
3.1 概述
-
是 java 一个关键字,表示 最终的意思。
- 可以修饰类,方法,成员变量(属性)。
-
设计初衷:java出现继承后,子类可以更改父类的功能,当父类功能不允许子类修改的时候,可以变成用
final
关键字修饰父类。
3.2 特点
- 被
final
修饰的类,不能被继承。 - 被
final
修饰的方法,不能被重写。 - 被
final
修饰的变量,是个常量,值不能被修改。- 常量的定义
final String ID="1109031989xxxxxxx";
- 常量的定义
3.2.1 入门案例
- final 修饰的常量名称,建议大写(如:TIME_VALUE),且不能被修改。
- 例如:中国建国日期,就是一个固定不能被修改的日期
TIME_VALUE
大写。
- 例如:中国建国日期,就是一个固定不能被修改的日期
/* final测试类*/
public class FinalDemo {
public static void main(String[] args) {
son s = new son();
s.speak(); // 方法不能被重写只能用父类
//s. TIME=""; // final修饰的就是常量, 常量不能被修改。
s.food();
System.out.println(s.TIME_VALUE);
}
}
class Fu {
// final class Fu 修饰类名则该类不能被继承
int age;
final String TIME_VALUE = "1949年10月1日";
public final void speak(){
System.out.println("说家乡话...");
}
public void food(){
final String name="中华美食";
// name=".."; // 修改是会报错的!
System.out.println(name);
}
}
class son extends Fu{
@Override
public void food() { // 普通方法可以重写
System.out.println("中华人民美食");
}
}
-
输出结果:
说家乡话... 中华人民美食 1949年10月1日
- 思考一个问题: final 修饰的引用对象! ?? 会怎么样!?
final String[] arr = new String[2]
4. 多态
4.1 什么是多态 ?
- 面向对象第三大特征之一,指同一个实体同时具有多种形式, 即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
- 例如:水 , 在固态的情况下: 冰。 气态情况下: 水蒸汽。 液态下: 水。
“同一个对象,在不同时刻,代表对象不一样”
- 例如:水 , 在固态的情况下: 冰。 气态情况下: 水蒸汽。 液态下: 水。
- 可以把不同的子类对象都当作父类来看 (父类的引用指向子类对象),进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
- 例如 : 键盘上得 按键F1 在不同的程序中有不同的作用,在word中是弹出是帮助, 在截屏软件中是截屏, 这就是在同一个事件在不同的对象中产生不同的结果。
我们只是通过F1这一个父类就能够引用不同的子类,这就是多态我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
- 就是说让,父类型的引用就可以根据,当前赋值给它的子对象的特性以不同的方式运作。就好比 F1按键是通用的父类按键,根据子类的对象需求,以更像子类对象方式运作。
- 例如 : 键盘上得 按键F1 在不同的程序中有不同的作用,在word中是弹出是帮助, 在截屏软件中是截屏, 这就是在同一个事件在不同的对象中产生不同的结果。
4.2 多态特点(满足3个条件)
- 多态的 前提是继承。
- 要有 方法重写。
- 父类引用指向子类对象
- 如:
Animal a = new Lion() ; //向上转型。 小变大
。多态对象只能调用,父类中的方法,被子类重写才能调用。并不能调用子类自己的方法。 - 心法: 编译看左边 , 运行看右边。
- 如:
4.2.1 多态 入门案例
- 案例需求: 钱能买东西,在不同的国家需要使用不同的币种,如中国就是人民币,美国就是美元。
- 一定要满足3个条件:
继承 重写 ,父类引用指向子类对象。
- 一定要满足3个条件:
// 测试类
public class DuoTaiDemo {
public static void main(String[] args) {
//1. 正常的方法调用
Money m = new Money();
m.buy(); // 调用自己买东西方法
RMB rmb = new RMB();
rmb.buy(); // 调用重写父类的方法;
// 1. 去国外旅游 去日本,需要使用日元。怎么处理?
//多态的重要性就体现出来了。 是一种底层设计思想。不能看中代码要看抽象思想。
/**
* 1. 有继承关系
* 2. 方法重写
* 3. 父类引用指向子类对象。
*/
Money money = new JPY();
/**心法: 编译看左边,执行看右边!
* 涉及到java执行的流程图。
* 编译的时候执行的是父类的方法。
* 运行时,看右边对象实现的方法,最后的效果!
*/
money.buy(); //需要日元消费
//思考:如果在韩国旅游怎么处理?
// TODO: 继承父类,重写方法,向上转型。 多态是一种思想!!!!
}
}
/*钱或者货币能买东西*/
class Money{
public void buy(){
System.out.println("购物");
}
}
// 在中国你使用的是 RMB
class RMB extends Money{
@Override
public void buy() {
System.out.println("使用人民币消费");
}
}
// 日本
class JPY extends Money{
// 重写父类方法,思想,相当于兑换货币
@Override
public void buy() {
System.out.println("使用日元消费");
}
}
// 韩国
class KWD{
}
4.3 多态的好处
- 多态可以让我们不用关心某个对象到底是什么具体类型,就可以使用该对象的某些方法。
- 体现在底层设计,传递参数上。
- 提高了程序的扩展性和可维护性。
public class DuoTaiDemo {
/*1.多态的好处,可以把不同的子类对象都当作父类来看
不需要关注子类,具体什么类型。
*/
public void buy(Fruit f){
f.info(); // Fruit f = new Apple();
}
public static void main(String[] args) {
DuoTaiDemo d = new DuoTaiDemo();
/* 需要水果,主要为了减肥。
*/
d.buy(new Apple());//需要什么就传递相应对象。
// TODO 如果需要一个西瓜怎么搞?
}
}
class Fruit{
public void info() {
System.out.println("补充水分");
}
}
class Apple extends Fruit{
@Override
public void info() {
System.out.println("减肥,并补充维生素 C");
}
}
class Banana extends Fruit{
@Override
public void info() {
System.out.println("润肠,还可以增强肌肉");
}
}
4.4 多态的使用
4.4.1 注意事项
- 成员变量:使用的是父类的;
- 成员方法: 因为重写,所以使用时子类的;
- 静态成员: 随着对象的存在而存在,谁调用就返回谁!
- 静态修饰的方法不能重写,即使是重名也不存在重写。
4.4.2 测试案例
- 多态中的调用子类的方法,必须在父类存在。
- 如果 想使用自己的方法 就需要 向下转型。
public class DuoTaiDemo {
public static void main(String[] args) {
//1.多态形成
Fruit f = new Apple();
System.out.println(f.num);//多态调用的是父类的成员
f.info();//编译看左边,执行看右边。
System.out.println(f.Fruit_NAME);
f.show(); // 多态中,只能调用父类有的方法,静态不能被重写,谁调用就是谁。
//2.如果想使用子类中自己的方法就需要向下转型。
Apple apple = (Apple) f;
apple.show(); //向下转型调用自己的方法
}
}
class Fruit{
int num=10;
static String Fruit_NAME="水果";
public void info() {
System.out.println("补充水分");
}
public static void show(){
System.out.println("每天都要吃水果");
}
}
class Apple extends Fruit{
int num=20;
static String Fruit_NAME="苹果";
@Override
public void info() {
System.out.println("减肥,并补充维生素 C");
}
public static void show(){
System.out.println("一天一颗苹果,疾病远离我");
}
}
-
输出结果:
10 减肥,并补充维生素 C 水果 每天都要吃水果 一天一颗苹果,疾病远离我
5.知识拓展
5.1 静态变量 和 实例变量 的区别
- 静态变量和实例变量都叫成员变量。
- 在语法定义上的区别 :
- 静态变量,前要加static关键字,没有static的叫做实例变量,实例变量前则不加。
- 在程序运行时的区别 :
- 实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
- 静态变量不属于某个实例对象,而是属于类,所以也可以叫 类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
- 使用上区别:
- 实例变量必须创建对象后才可以通过这个对象来使用,
- 静态变量则可以直接使用类名来引用。
5.2 向上转型和向下转型
-
在java中,继承 是一个重要的特征,通过
extends
关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。 -
在应用中就存在着两种转型方式,分别是:向上转型和向下转型。
-
例如:父类Fu,子类Son
- 向上转型:父类的引用指向子类对象
Fu=new Son();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类的方法就根据这个引用指向调用子类重写方法。 - 向下转型(较少):子类的引用的指向子类对象,过程中必须要采取到强制转型。
Fu f = new Son(); //向上转型,此时,f是Fu类型
Son s = (Son)f; //此时,把Fu类型的 f 转成小类型Son
在应用中就其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
public class Demo { public static void main(String[] args) { Fu2 f = new Son2(); f.eat(); Son2 s = (Son2)f; s.speak(); // 相当于直接new Son2 不好么!? } } class Fu2{ public void eat(){ System.out.println("FU EAT"); } } class Son2 extends Fu2{ @Override public void eat() { System.out.println("Son EAT"); } public void speak(){ System.out.println("Son speak!"); } }
- 向上转型:父类的引用指向子类对象