对象与类(二)
1.8 static关键字
成员变量+static=静态变量
当我们在设计类的时候 发现多个对象中有共享数据的时候 我们就可以把这个共享的数据 定义为静态的
像name age虽然每个对象都有 ,但是值不一定一样 对象的特有数据->成员变量
像country虽然每个对象也都有 ,但是值一样 对象的共有数据-> 静态变量
上述可以知道 共有的数据存在于一个特定的空间→── 静态方法区
成员函数+static=静态函数
静态函数意味着就是对象的共有行为吗?不行的
我们当前的成员函数如果不访问任何成员变量的情况下 这个函数就可以定义为静态的
这就意味着 静态函数不能直接调用当前类中的成员变量 无法从静态上下文中引用非静态
为啥?静态的生命周期要长于对象
所以 静态是优先于对象存在的
静态也称之为是类的成员,我们可以直接通过类去调用
类.静态成员
class StaticDemo{
public static void main(String[] args){
Chinese c1=new Chinese();
Chinese c2=new Chinese();
Chinese c3=new Chinese();
//通过 对象.变量
//1.先去堆内存中对象的所属空间里找
System.out.println(c1.name); //成员变量
//2.再去静态方法区中该对象所属类的空间里找
System.out.println(c1.country); //静态变量
//通过 对象.函数
//1.先去非静态方法区中该对象所属类的空间里找
c1.show();
//2.再去静态方法区中该对象所属类的空间里找
c1.haha();
System.out.println(Chinese.country);
Chinese.haha();
//Chinese.show();
System.out.println(Chinese.momo);
System.out.println(Chinese.kaka);
}
}
class Chinese{
static int momo;
static boolean kaka;
String name; //姓名
String age; //年龄
static String country="China"; //国籍
//无法从静态上下文中引用非静态
public void show(){
String name="旺财";
/*
成员函数内部在调用变量时的顺序:
1.先找局部变量
2.去堆中对象空间中找
3.去静态区该对象所属类的空间里找
我要喝水 水从哪里来
1.如果你的嘴里已经有水 直接喝
2.如果你的嘴里没水 杯子里有水 拿杯子喝
3.如果你的嘴里没水 杯子没水 去饮水机
(先不考虑继承的情况下)
*/
System.out.println(name+age+country);
}
public static void haha(){
System.out.println("哈哈");
}
public static void main(String[] args){
}
}
为什么主函数是静态的?
主函数是程序的入口,优于其它运行
假设主函数是非静态的 那么必须先创建对象 才能调用主函数
静态变量有没有默认初始化值?有 (运行时常量 永久代 JVM)
也有显示初始化
类的分类:
实体类:
就是为了描述一个事物 Person Point
类中内容基本上都是成员函数/成员变量
也会而存在一个静态成员
工具类
提供一些已经被实现好的功能 向外部供应
所以工具类中基本上全都是静态函数
类.Xxx 类.Xxx() Math Arrays
为啥工具类中都是静态?
1.共有的
2.长时间存在
3.加载一次 后期随便使用
一个道理:
当我们钉一个钉子的时候 找榔头
你是造一个榔头 还是用已存在的榔头?
如果工具类可以被创建对象 是不是就想用的时候去创建
效率较低
主类/测试类
主要用于运行/测试代码
这个类中会有主函数的存在
实体类是可以存在主函数的 但是不推荐
工具类一般不会存在主函数
1.9 对象的内存图解III
步骤:
1.javac 将Java源代码进行编译 生成字节码文件
2.java 将字节码文件加载进虚拟机中 开始运行程序
字节码具体被加载在方法区里面
方法区可以大致分为 非静态方法区/静态方法区
所有非静态的代码(二进制)内容加载进非静态去
所有静态的代码(二进制)内容加载进静态区
JVM接下来会根据参数(java 字节码文件名(类名)) 去静态区找主函数
将主函数代码加载进虚拟机栈 开始运行
3.后面的和之前一样
1.10 静态变量与成员变量的区别
1.生命周期
成员变量随着对象的创建而创建 随着对象的消亡而消亡
静态变量随着类的加载而创建 随着程序结束而消失
2.调用方式
成员变量必须先创建对象 在通过对象去调用
静态变量可以被对象调用 也可以直接用类调用
3.存储位置
成员变量存在于堆内存中对象的所属空间里
静态变量存在于静态方法区中类的所属空间里
4.命名
成员变量-对象的特有属性
静态变量-对象的共有属性 类成员
1.11 代码块
代码块 { ... }
局部代码块:存在于函数当中(包括函数) for(){...} if(){...}
构造代码块:直接在类中出现的{...}
当对象创建一次 构造代码块执行一次
作用等同于构造函数
静态代码块:直接在类中出现的static{...}
当类被加载的时候 仅且只执行一次
作用于 对类进行一些初始化操作 JDBC
1.12 单例模式
设计模式:就是我们的前辈们总结出来的一些编码技巧
它并不是随着Java的诞生而诞生的
它是由Java的广大使用者总结出来的一套编码经验
常见26种
单例模式:使用场景是 某一个类只能创建一个对象
比如某一个朝代的皇帝 只能是唯一的
1.既然只能创建一个对象的话 就得不能让外界去创建对象
限制使用new不现实
只能从对象的创建流程中考虑 只要有一个步骤不行 对象就创建不出来
开辟空间分配地址 是由计算机底层决定 我们也控制不了
构造函数执行 只需要将构造函数私有化即可
2.既然外界不能创建对象 我们还得保证对象的创建
所以我们只能在类内部创建对象
Single s=new Single();
能否写成 成员变量的形式?
所以private static
//饿汉式 比较急 所以直接返回对象
/*
class Single{
private static Single s=new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}
*/
//饱汗式 比交满 不急 判断一下
class Single{
private static Single s;
private Single(){}
public static Single getInstance(){
if(s==null){
s=new Single();
}
return s;
}
}
class SingleTest{
public static void main(String[] args){
Single s1=Single.getInstance();
Single s2=Single.getInstance();
//s2=null;
Single s3=Single.getInstance();
System.out.println(s1==s2);
System.out.println(s2==s3);
}
}
3.内部创建出对象 还得向外界提供
因为private 外界不能直接访问
所以见解 向外界提供一个函数 外界通过调用函数获取对象
1.13 继承
继承由来是什么:
当我们在定义若干个类的时候,发现某一些类中具有相同的属性和行为
那么,我们就可以将这些相同的部分进行抽取,独立生成另外一个类,那么这个生成出来
的类我们称之为是父类,其他的被抽取内容的类称之为子类
子类与父类之间就是所谓的继承关系 用 extends来表示
那么在现实生活中,父与子之间的一对多 还是多对多的关系?
所以 在Java当中 继承是单继承(一个子类只能有一个父类,但是一个父类可以有若干个子类)
当然,在C++中,继承是多继承的,不太符合实际的社会问题,所以Java更加符合现实编程
(不代表Java当中就没有多继承的时候!如果是类与类之间 必须是单继承;如果是接口与接口之间,可以是多继承)
既然有了继承,那么在Java当中就有了继承体系
(既然有了父子关系,是不是就产生出了族谱这个东西)
先说一点:我们常用的继承体系 异常体系 集合体系 IO体系 网络体系 多线程体系
class ExtendsDemo02{
public static void main(String[] args){
Zi zi=new Zi();
System.out.println(zi.num1);//20
System.out.println(zi.num2);//30
System.out.println(zi.num3);//50
zi.show();
zi.sing();
}
}
//子父类中成员变量的特点
/*
class Grandfather{
int num1=100;
int num4=200;
}
*/
class Fu {//extends Grandfather{
int num1=10;
static int num2=40;
static int num3=50;
Fu(){
super();//Object(){} 显示初始化父类空间中的成员变量
System.out.println("Fu constructor1......");
}
Fu(int num){
System.out.println("Fu constructor2......");
}
void sing(){
System.out.println("Fu sing......");
System.out.println("爸爸我会唱红歌");
}
}
class Zi extends Fu{
int num1=20;
static int num2=30;
Zi(){
this(0,0);
System.out.println("Zi constructor1......");
}
Zi(int num1){
this(0,0);
System.out.println("Zi constructor2......");
}
Zi(int num1,int num2){
super();
System.out.println("Zi constructor3......");
}
void show(){
//局部变量和成员变量如果重名了 this区分
System.out.println(num1+","+num2+","+num3);
//局部变量/成员变量和父类变量重名了 super区别 super.xxx => 你爸爸的xxx
System.out.println(super.num1+","+super.num2+","+num3);
//System.out.println(super.super.num1);
//System.out.println(num4);
}
@Override
void sing(){
System.out.println("Zi sing......");
System.out.println("儿子我会唱摇滚、民谣、爵士、蓝调、电子");
}
}
在Java继承体系当中,所有类的最终父类都是Object!Object就不存在爸爸了
如果我们在定义一个类的时候,没有显著声明父类的时候 那么该类的父类默认是Object
注意一点:在分析设计继承的时候 一定要符合社会常识问题
子类 和 父类 之间 必须是 is a 关系 子类必须父类的一种 同一系列的
she is a Girl ; he is a boy ; he is a ladyboy ; 旺财 is a Dog
所以,千万不要为了获取某一个类的功能而把这个类叫爸爸!
子父类中成员变量/静态变量的特点:
如果只有子类有且非私有 那么就调用子类的
如果只有父类有且非私有 那么就调用父类的
如果子父类都有且非私有 那么就调用子类的
(成员变量之间 是不存在重写关系的!!!)
子类.属性 顺序:子类对象成员->子类静态->父类成员->父类静态
子类成员函数在内部调用变量时 局部变量->子类对象成员->子类静态->父类成员->父类静态
子父类中构造函数的特点:
现象:子类的构造函数在调用运行的时候 先执行父类的构造函数
在子类的构造函数当中,有一句默认的super(...)隐藏了,而且这句代码必须是在第一行
对super的调用必须是构造器中的第一个
为什么?道理很简单 儿子向老爸要钱,那么老爸是不是得先准备一下钱
既然子类继承自父类 那么必然会继承到父类当中的一些数据
所以,在子类构造函数之中,必须先让父类把这些数据进行一些初始化才能继承给子类
注意:父类的构造函数被调用,但不代表父类就被创建对象了!
所以this是当前对象的引用,而super不是父类对象的引用,而是父类空间的引用
并且注意super(...) 如果父类没有默认无参构造函数 那么子类构造函数中super()失效了
所以在调用父类构造函数时,一定要注意父类构造函数的参数情况!适时修改super(...)
this(...)是当前类中 本类构造函数调用本类构造函数的方式
super(...)是本类构造函数调用父类构造函数的方式
都必须在第一行 那么这两冲突不?
如果本类构造函数当中不存在调用关系 那么每一个本类构造函数第一句都是super(...)
如果本类构造函数当中存在调用关系,那么最后被调用的那个构造函数第一句绝对是super(...)
this(...)的调用是单向的还是递归的?是单向的,那么最后被调用的第一句绝对就是super(...)
子父类中成员函数的特点:
如果只有子类有且非私有 那么就调用子类的
如果只有父类有且非私有 那么就调用父类的
如果子父类都有且非私有 那么就调用子类的(函数重写)
函数重写:
在子父类中,同名函数
函数有什么组成:函数声明(权限 类型 返回值类型 函数名 参数列表)+函数体({}里面的内容)
重写的意义在于哪?在于子类继承了父类的函数声明(功能),但是子类可以将该函数的具体实现进行优化或更改1.保留父类的功能声明 子类可以进一步优化
2.保留父类的功能声明 子类可以重新定义
重写当中应该注意到的一些细节:
1.函数重名 但是参数列表不同 不构成重写关系
2.函数重名 参数列表相同 构成重写关系 返回值类型也必须是一样的
3.子类重写父类函数时,权限>=父类权限
4.当然 如果父类函数权限为private 子类压根就继承不到 何谈重写
1.14 抽象类
抽象类:模糊不清的类 不具体的类
当我们在抽取一个父类的时候,发现子类当中的一些共同方法在父类中无法进行具体的实现
并且这些方法只能在子类中具体实现时,父类当中的这些函数就只保留函数声明即可,不必写函数体
那么此时这个函数就是 抽象函数! 有抽象函数的类 就是抽象类!
特点:
1.抽象类能否创建对象?不能 对象本身是一个具体的东西 而抽象类中含有不具体的内容
2.抽象类必须作为父类存在吗?必须 抽象类就等着被继承呢!
3.抽象类和一般类有什么区别?就是一般类的特殊情况
唯一的区别只在于抽象类中有抽象函数!
4.抽象类中必须有抽象函数吗?不一定 AWT界面开发
5.abstract这个关键字不能和那些关键字共存?
private 抽象函数就等着被子类重写实现呢!
static 静态是优先于对象存在的,在加载静态数据的时候 肯定是具体的数据
1.17 多态
多种状态:就是指一个对象可以有多种状态(他在继承体系中的位置变化)
D->C->B->A->Animal
位置的变化只能是向上变 但不能低于自身
对于C而言 可以当做B来看 也可以当做A来看 也可以当成Animal
小明 男人 人 动物
小明的学校开家长会 小明不想让他爸爸去 他让大明去 大明就是小明他哥
大明冒充Dad去参加家长会的话,那么所表现出来的行为 是不是应该符合他爸行为
不同的视角 不同的角色
无论是在哪个视角场合 对象本身变了没有?只不过我们需要对象在不同的场合表现出相对应的行为
在Java当中 多态代码表现 就是 父类引用指向子类的对象
class DuoTaiDemo01{
public static void main(String[] args){
//父类引用指向子类的对象
Dady d=new Son();
d.chouyan();
d.hejiu();
d.tangtou();
//d.lol();
System.out.println(d.num);
d.xiToufang();
}
}
class Dady{
int num=20;
void chouyan(){
System.out.println("爸爸抽烟~");
}
void hejiu(){
System.out.println("爸爸喝酒~");
}
void tangtou(){
System.out.println("爸爸烫头~");
}
void xiToufang(){
System.out.println("爸爸洗头房~");
}
}
class Son extends Dady{
int num=10;
void chouyan(){
System.out.println("儿子抽烟~");
}
void hejiu(){
System.out.println("儿子喝酒~");
}
void tangtou(){
System.out.println("儿子烫头~");
}
void lol(){ //对象的特有行为
System.out.println("儿子玩lol~");
}
}
多态的前提:继承 重写
多态的好处:对代码的功能进行了可扩展性
子类对象可以当做父类对象去使用 但是有限制 只能调用父类函数或重写的函数
不能使用子类特有的行为
多态当中 成员变量的特点:只能访问父类中的成员变量
多态当中 成员函数的特点:
如果子类没有重写 且父类中有 调用父类的
如果子类有重写 调用子类重写的
如果不存在重写 父类没有 那就报错了!
class DuoTaiDemo02{
public static void main(String[] args){
Dog d=new Dog();
Cat c=new Cat();
Pig p=new Pig();
feed(d);
feed(c);
feed(p);
}
//Animal a=new Dog();
//Animal a=new Cat(); 向上类型转换
public static void feed(Animal a){
a.eat();
//ClassCastException 类型转换异常
//判断对象的本质类型 instanceOf
if(a instanceof Pig){
Pig pp=(Pig)a;
pp.gongBaiCai();
}
}
/*
public static void feed(Pig p){
p.eat();
}
public static void feed(Cat c){
c.eat();
}
public static void feed(Dog d){
d.eat();
}
*/
}
abstract class Animal{
static abstract void eat();
abstract void jiao();
}
class Pig extends Animal{
void eat(){
System.out.println("猪啥都吃......");
}
void jiao(){
System.out.println("猪哼哼哼......");
}
void gongBaiCai(){
System.out.println("猪拱白菜......");
}
}
class Dog extends Animal{
void eat(){
System.out.println("狗吃狗粮......");
}
void jiao(){
System.out.println("狗汪汪汪......");
}
void lookDoor(){
System.out.println("狗看家~......");
}
}
class Cat extends Animal{
void eat(){
System.out.println("猫吃鱼......");
}
void jiao(){
System.out.println("猫喵喵喵......");
}
void catchMouse(){
System.out.println("猫捉老鼠~......");
}
}
上一篇: 什么是Jitter和Beta
下一篇: SQL入门练习