1. static
1.1 概述
在java中可以通过static实现一个全局概念,static修饰的资源称为静态资源。static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,当然也可以修饰代码块。
1.2 作用
- 可以修饰成员变量、成员方法。
- 优先于对象存在。即:静态是先存在,对象是后存在。
- 静态资源可以用类名或者对象进行调用,非静态只能用对象。
- 全局只有一份,在全局共享。
- 静态区域内,不允许使用
this和super关键字
。(参考第二点?那时可能还没有对象)
1.3 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.4 简单介绍:静态方法内存图
- 只需要简单理解即可。
- 内存划分简单理解为三个区域,
栈区域,堆区域,方法区区域(普通方法区,静态方法区)。
- 当类加载的时候,静态资源和main都在静态方法区,普通方法在普通方法区,这两个方法区的资源是共享的,非静态区的资源只能被对象调用,静态区有类名去调用,也可以用对象调用。加载完方法之后会生成该类方法区的标识即地址值 0x0012,
- Person.speak();相当于类加载了,开始开辟空间,而非静态需要创建完对象才开辟空间。
- 栈中的p就是保存了堆内存的地址空间,通过地址值去依次查找,如方法,如变量。(当对象创建完成之后,会生成地址值)
1.5 练习: 静态调用关系
- 静态只能调用静态资源,非静态可以访问所有。
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. 静态代码块,构造代码块,局部代码块
- 啥是代码块?
在java中用{}括起来的称为代码块
2.1 静态代码块
- 用
static{}
包裹起来的代码片段,随着类的加载而加载,并且只会被加载一次。 - 作用:一般用于项目初始化。
2.2 构造代码块
- 构造代码块是在
类中方法外用 {} 括起来的代码
,抽取出来。在调用构造方法的时候会去自动调用构造代码块。
2.作用:主要是用于提取所有构造方法中的共性功能, 构造代码快优先于构造方法。
2.3 局部代码块
- 是在
方法名后面用 {} 括起来的代码段
。 - 作用:是限定变量的生命周期和提高效率。
2.4 代码块测试
- 思考? 那么执行顺序是怎么样的?
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 数据类型 常量名 = 值
3.3 入门案例
- final 修饰的常量名称建议大写(java规范),且不能被修改。
/* 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 Fu f = new Fu();
4. 多态
- 面向对象第三大特征之一,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
- 可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
- 水 , 在固态的情况下: 冰。 气态情况下: 水蒸汽。 液态下: 水。
“同一个对象,在不同时刻,代表对象不一样”
- 例如:键盘上得 按键F1 在不同的程序中有不同的作用,在word中是弹出是帮助, 在截屏软件中是截屏, 这就是在同一个事件在不同的对象中产生不同的结果。
我们只是通过F1这一个父类就能够引用不同的子类,这就是多态我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
java的多态怎么体现呢?
Lion l = new Lion();//狮子是狮子
。Animal a = new Lion()// 狮子是动物,创建了狮子的对象,赋值给动物对象,这种形式就是多态
。
4.1 多态特点(满足3个条件)
- 多态的前提是继承。
- 要有方法重写。
- 父类引用指向子类对象 如:
Animal a = new Lion() ; //向上转型。 小变大
。
心法: 编译看左边 , 运行看右边。
4.2 入门案例
一定要满足3个条件: 继承 重写 ,父类引用指向子类对象。
// 测试类
public class DuoTaiDemo {
public static void main(String[] args) {
/**
*
*/
Money m = new Money();
m.buy(); // 调用自己买东西方法
RMB rmb = new RMB();
rmb.buy(); // 调用重写父类的方法;
// 假设 我是中国人我去小樱花家买鞭炮炸它,这时候需要花钱?RMB? JPY?
//多态的重要性就体现出来了。 是一种底层设计思想。不能看中代码要看抽象思想。
/**
* 实现多态的意义了!
* 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 DuoTaiGoods {
/* //提供小狗过来吃,只能小狗吃,小猫能吃么?不能
public void eat(Dog dog){
dog.eat();
}
public void eat(Cat cat){
cat.eat();
}
*/
// 有非常多的小动物要吃东西!?
// TODO 是不是需要写很多这样的方法体...有没有解决方案!?
//多态 。。
public void eat(Animal a){
a.eat(); // 这个地方就用了多态的思想: 相当于 Animal a = new 动物();
}
public static void main(String[] args) {
/* //只能小狗吃,小猫能吃么? 吃不了!!
DuoTaiGoods duoTaiGoods = new DuoTaiGoods();
duoTaiGoods.eat(new Dog());
*/
DuoTaiGoods duoTaiGoods2 = new DuoTaiGoods();
// 不在关心具体的子类类型,把子类当做父类来看
duoTaiGoods2.eat(new Cat()); // 传递小猫, 小狗, 等等小动物都可以 new Dog();
}
}
/**
* 1 当相同的方法抽取共同,直接可以使用父类方法。
*/
class Animal{
public void eat(){
System.out.println("吃东西!");
}
}
class Dog extends Animal{
// 2后来发现子类吃的跟父类吃的不一样。
public void eat(){
System.out.println("eat 骨头!"); // 重写方法体。
}
}
class Cat extends Animal{
public void eat(){
System.out.println("eat 小鱼!");
}
}
输出结果:
eat 小鱼!
4.4 多态的使用
4.4.1 特点
- 成员变量:使用的是父类的;
- 成员方法: 因为重写,所以使用时子类的;
- 静态成员: 随着对象的存在而存在,谁调用就返回谁!!
4.4.2 测试案例
public class DuoTaiTest {
public static void main(String[] args) {
// TODO 创建子类成员测试:
Son s = new Son();
/*成员变量: 子类没有就使用父类,子类有就使用子类; */
System.out.println(s.sum); // 就近原则;
s.eat(); // 同成员变量一样,如果自己有使用自己,没有则使用父亲。
// TODO 创建多态测试
// 继承+方法重写
FU f = new Son();
System.out.println(f.sum); //多态使用的是父类的。执行看左边。
s.eat(); // 编译看左边(功能父类提供的),执行看右边。 使用子类方法!
// TODO 静态方法测试
f.speak(); // 得出什么样的结论!!?!?
System.out.println(f.count);
System.out.println(s.count);
}
}
// 父类
class FU{
int sum = 20; // 多态情况如果父类没有就会报错,执行看左边
static int count = 20;
//测试方法:
public void eat(){
System.out.println("吃辛苦长大!");
}
//测试静态方法
public static void speak(){
System.out.println("说得中国话!");
}
}
//子类
class Son extends FU{
int sum = 10;
static int count = 10;
// 如果想实现多态就需要重写
@Override
public void eat() {
System.out.println("吃健康,快乐长大!");
}
// 注意: 静态修饰的方法,不存在重写! 谁调用就是谁!
public static void speak(){
System.out.println("son 说得中国话!");
}
}
输出结果:
10
吃健康,快乐长大!
20
吃健康,快乐长大!
说得中国话!
20
10
5.知识拓展
5.1 静态变量和实例变量的区别
成员变量就是类里面的变量,不区分static。 静态变量和实例变量都叫成员变量。
在语法定义上的区别:
静态变量–前要加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!");
}
}
5.3 小结-- 自己动手
假设现在有一个交通工具类。摩托车,汽车,自行车 …
1、 创建交通工具类。提供基本功能 如: 启停功能, 加速功能等
2、 创建子类,继承交通工具类。 选择相应的方法进行重写!
3、 创建多态对象,进行功能测试。
/**
* 父类
*/
public class Transportation {
private String name; //什么类型的车
private int num; // 车架号
// 方法
public void powerOnAndOff(){
System.out.println("车辆正常驾驶!");
}
/*车最快马力*/
public void speed(){
System.out.println("速度,马匹!");
}
//提供对外访问方法:set 和 get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
// 统一工具 如果想使用类名调用 static
// static public void show(Transportation t){}
public void show(Transportation t){
t.speed();
}
}
/**
* 交通工具子类
*/
public class MotoCar extends Transportation{
@Override
public void speed() {
//TODO
super.setName("Suzuki");
System.out.println(getName()+"cvt 125cc");
}
}
// TODO 油价上涨... 改骑自行车了!
class Bike extends Transportation{
@Override
public void speed() {
super.setName("二八大杠");
System.out.println(getName()+"取决于,是否吃饱饭...");
}
}
class Demo{
public static void main(String[] args) {
// //TODO 对象测试略!
// // 看天气出门 想骑啥骑啥!
// Transportation.show(new Bike());
// Transportation.show(new MotoCar());
Transportation t = new Bike();
t.speed();
Transportation t1 = new Transportation();
// 传递对象符合多态条件。
t.show(new MotoCar()); // 多态看传递对象是谁 t.show(new Transportation()) //调用自己方法!
}
}
输出结果:
二八大杠取决于,是否吃饱饭...
Suzukicvt 125cc