面向对象
static 关键字
被static关键字修饰的方法一般被称为静态方法; 没有被static关键字修饰的方法 一般被称为非静态方法。
同理,被static关键字修饰的成员变量叫静态成员变量(成员变量+static=静态变量) ,没有被static修饰的叫非静态成员变量。
- 静态方法--只能访问静态的成员变量(如果访问非静态成员变量 那么无法通过编译)
无需创建对象,使用类名就可以直接调用
- 非静态方法--既能访问静态的成员变量,也能访问非静态的成员变量
只能通过创建对象的方式进行调用
- 静态的成员变量和静态的方法 无需创建对象通过类名进行访问
- 静态方法和静态成员变量 被整个程序共用 相当于共享
- 被static修饰的内容 在代码程序中会按照顺序进行加载, 如果主函数在下面,那么会优先加载静态成员变量和成员方法
灵魂拷问:为什么主函数是静态的?
主函数是程序的入口,优于其它运行
被static修饰的关键字都会优先加载
public class StaticTest {
private static StaticTest st = new StaticTest();
private static int count1;
private static int count2 = 0;
public StaticTest() {
count1++;
count2++;
}
public static StaticTest getInstance(){
return st;
}
public static void main(String[] args) {
StaticTest st = StaticTest.getInstance();
System.out.println("count1:"+count1);
System.out.println("count2:"+count2);
}
}
结果为:1
0
代码块({ ... })
局部代码块:存在于函数当中(包括函数) for(){...} if(){...}
构造代码块:直接在类中出现的{...}
当对象创建一次 构造代码块执行一次(作用等同于构造函数)
静态代码块:直接在类中出现的static{...}
在程序中只执行一次,被static关键字所修饰的内容是按照代码的顺序来进行调用的
- 当类被加载的时候 仅且只执行一次 (作用:对类进行一些初始化操作)
static{
//代码
}
例如:
static{
System.out.println("000");
}
注意:
1.静态函数中,不可能存在this关键字
2.静态变量,我们在定义的时候,一般而言这个变量是不可修改的 加上final修饰
类的分类:
实体类:就是为了描述一个事物
类中内容基本上都是成员函数/成员变量;也会而存在一个静态成员
工具类:提供一些已经被实现好的功能 向外部供应
工具类中基本上全都是静态函数(因为:1.共有的 2.长时间存在 3.加载一次 后期随便使用)
主类/测试类:主要用于运行/测试代码;这个类中会有主函数的存在
工具类一般不会存在主函数
静态变量与成员变量的区别
1.生命周期
成员变量随着对象的创建而创建 随着对象的消亡而消亡
静态变量随着类的加载而创建 随着程序结束而消失
2.调用方式
成员变量必须先创建对象 在通过对象去调用
静态变量可以被对象调用 也可以直接用类调用
3.存储位置
成员变量存在于堆内存中对象的所属空间里
静态变量存在于静态方法区中类的所属空间里
4.命名
成员变量-对象的特有属性
静态变量-对象的共有属性 类成员
成员函数内部在调用变量时的顺序:
1.先找局部变量
2.去堆中对象空间中找
3.去静态区该对象所属类的空间里找
成员变量和局部变量的区别
1.在类中位置不同
成员变量在类中方法外
局部变量在方法定义中或方法声明上
2.内存中的位置不同
成员变量在堆内存
局部变量在栈内存
3.生命周期不同
成员变量随着对象的创建而存在,随着对象的消失而消失
局部变量随着方法的调用而存在,随着方法的调用完毕而消失
4.初始化值不同
成员变量有默认值初始化
局部变量没有默认值初始化,必须定义、赋值,然后才能赋值
单例模式
注意:使用场景:某一个类只能创建一个对象
1.既然只能创建一个对象的话 就不能让外界去创建对象
限制使用new不现实
只能从对象的创建流程中考虑 只要有一个步骤不行 对象就创建不出来
开辟空间分配地址 是由计算机底层决定 我们也控制不了
构造函数执行 只需要将构造函数私有化即可
2.既然外界不能创建对象 我们还得保证对象的创建
所以我们只能在类内部创建对象
Single s=new Single();
所以private static
3.内部创建出对象 还得向外界提供
因为private 外界不能直接访问
所以见解 向外界提供一个函数 外界通过调用函数获取对象
继承
- 继承(面向对象的特征之一):当我们在定义若干个类的时候,发现某一些类中具有相同的属性和行为那么,我们就可以将这些相同的部分进行抽取,独立生成另外一个类,那么这个生成出来的类我们称之为是父类,其他的被抽取内容的类称之为子类子类与父类之间就是所谓的继承关系 用 extends来表示。
父---子
父类: 被继承的类叫父类
子类: 去继承的类叫子类
is ---a 的关系
This is a dog
This is a Animal
对象和对象的关系上来讲:可以把类似狗和动物之间的这样的关系看成是一种继承的关系
从生活的角度去说:运动员 ----篮球运动员;交通工具 -----火车
!!!使用extends 关键字来实现类与类的继承关系!!!
//父类
public class Animal {
public String name;
public int legnum;
public void happy(){
System.out.println("叫");
}
}
//子类继承父类
public class Dog extends Animal{
}
继承关系特点:Java语言的继承关系:是一种单继承的关系
- 一个子类只能有一个父类,而一个父类可以有多个子类
- 子类可以可以使用父类的属性和方法 ,但是父类不能够访问子类独有的属性和方法
- 创建子类对象时 先执行父类的构造方法 ,然后在执行子类的构造方法
- 使用super关键字 去调用父类的构造方法(用法和this关键字类似)
注意:虽然Java是一种单继承的关系,但不代表Java当中就没有多继承的时候!
如果是类与类之间 必须是单继承;
如果是接口与接口之间,可以是多继承
有继承,那么在Java当中就有了继承体系;在Java继承体系当中,所有类的最终父类都是Object!
如果我们在定义一个类的时候,没有显著声明父类的时候 那么该类的父类默认是Object
注意一点:在分析设计继承的时候 一定要符合社会常识问题
子父类中成员变量/静态变量的特点:
- 如果只有子类有且非私有 那么就调用子类的
- 如果只有父类有且非私有 那么就调用父类的
- 如果子父类都有且非私有 那么就调用子类的
注意:成员变量之间 是不存在重写关系的!!!
子类成员函数在内部调用变量时 局部变量->子类对象成员->子类静态->父类成员->父类静态
子父类中构造函数的特点:
现象:子类的构造函数在调用运行的时候 先执行父类的构造函数,在子类的构造函数当中,有一句默认的super(...)隐藏了,而 且这句代码必须是在第一行,对super的调用必须是构造器中的第一个
注意:父类的构造函数被调用,但不代表父类就被创建对象了!
所以this是当前对象的引用,而super不是父类对象的引用,而是父类空间的引用
并且注意super(...) 如果父类没有默认无参构造函数 那么子类构造函数中super()失效了
所以在调用父类构造函数时,一定要注意父类构造函数的参数情况!适时修改super(...)
this(...)是当前类中 本类构造函数调用本类构造函数的方式
super(...)是本类构造函数调用父类构造函数的方式
都必须在第一行 那么这两冲突不?
如果本类构造函数当中不存在调用关系 那么每一个本类构造函数第一句都是super(...)
如果本类构造函数当中存在调用关系,那么最后被调用的那个构造函数第一句绝对是super(...)
this(...)的调用是单向的还是递归的?
是单向的,那么最后被调用的第一句绝对就是super(...)
子父类中成员函数的特点:
- 如果只有子类有且非私有 那么就调用子类的
- 如果只有父类有且非私有 那么就调用父类的
- 如果子父类都有且非私有 那么就调用子类的(函数重写)
重写override
满足条件:
- 一定发生在两个类中(父-子的关系 祖--孙的关系)一定是继承关系
- 访问修饰符相同 返回类型相同 方法名字相同 方法体不同 子类的访问修饰符不能比父类更严格
- 重写是多态的表现特征之一
重写的意义:在于子类继承了父类的函数声明(功能),但是子类可以将该函数的具体实现进行优化或更改
- 保留父类的功能声明 子类可以进一步优化
- 保留父类的功能声明 子类可以重新定义
重写当中应该注意到的一些细节:
- 函数重名 但是参数列表不同 不构成重写关系
- 函数重名 参数列表相同 构成重写关系 返回值类型也必须是一样的
- 子类重写父类函数时,权限>=父类权限
- 当然 如果父类函数权限为private 子类压根就继承不到 何谈重写
抽象类
抽象类:模糊不清的类 不具体的类
当我们在抽取一个父类的时候,发现子类当中的一些共同方法在父类中无法进行具体的实现,并且这些方法只能在子类中具体实现时,父类当中的这些函数就只保留函数声明即可,不必写函数体。那么此时这个函数就是 抽象函数! 有抽象函数的类 就是抽象类!
抽象类特点:
- 抽象类能否创建对象?(不能 对象本身是一个具体的东西 而抽象类中含有不具体的内容)
- 抽象类必须作为父类存在吗?(必须 抽象类就等着被继承呢!)
- 抽象类和一般类有什么区别?(就是一般类的特殊情况; 唯一的区别只在于抽象类中有抽象函数!)
- 抽象类中必须有抽象函数吗?(不一定 AWT界面开发)
- abstract这个关键字不能和那些关键字共存?
多态
面向对象的特征之一
对象拥有多种表现形态(他在继承体系中的位置变化)
位置的变化只能是向上变 但不能低于自身
多态的前提:继承 重写
多态的好处:
- 对代码的功能进行了可扩展性
- 子类对象可以当做父类对象去使用 但是有限制 只能调用父类函数或重写的函数
- 不能使用子类特有的行为
多态当中 成员变量的特点:只能访问父类中的成员变量
多态当中 成员函数的特点:
- 如果子类没有重写 且父类中有 调用父类的
- 如果子类有重写 调用子类重写的
- 如果不存在重写 父类没有 那就报错了!
在Java当中多态代码表现:父类引用指向子类对象
关于父类引用指向子类对象的应用
- Animal
public class Animal {
public String name;
public int legnum;
static{
System.out.println("This is static Animal");
}
public Animal(String name,int legnum){
System.out.println("This is Animal constructor");
this.name = name;
this.legnum = legnum;
}
/**
* 描述动物开心了 应该怎么叫
*
* 子类更具体 父类更抽象
*
*/
public String happy(){
return "叫";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getLegnum() {
return legnum;
}
public void setLegnum(int legnum) {
this.legnum = legnum;
}
}
- Dog
public class Dog extends Animal {
public String eyecolor;
static{
System.out.println("This is static Dog ");
}
public Dog(String name,int legnum){
super(name, legnum);
System.out.println("This is Dog constructor");
}
// 对子类独有的属性进行初始化
public Dog(String name,int legnum,String eyecolor){
super(name, legnum);
this.eyecolor = eyecolor;
System.out.println("This is Dog constructor");
}
public void f(){
System.out.println(name);
}
public String happy(){
return "wangwang!!!!";
}
}
- Cat
public class Cat extends Animal{
public Cat(String name,int legnum){
super(name, legnum);
}
@Override
public String happy() {
return "miaomiao !!!!!";
}
}
- Child
public class Child {
public String name;
public int age;
public Child(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 每个孩子都有自己的宠物 每一个宠物happy了应该怎么叫
*/
public void childPetHappy(Animal a){
System.out.println(this.getName()+"养了"+a.getName()+"这个宠物开心了"+a.happy()+"叫");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
Child c1 = new Child("lili", 10);
Child c2 = new Child("marry", 11);
Dog d= new Dog("honey", 4);
Cat c = new Cat("tom",4);
c1.childPetHappy(d);
c2.childPetHappy(c);
}
}
展示结果:
This is static Animal
This is static Dog
This is Animal constructor
This is Dog constructor
This is Animal constructor
lili养了honey这个宠物开心了wangwang!!!!叫
marry养了tom这个宠物开心了miaomiao !!!!!叫
引用数据类型转换
向上转型和向下转型:强制转换和自动转换
向上转型对应着:自动转换
向下转型对应着:强制转换
满足条件:两者之间要存在继承或者是实现关系(接口)
基本数据类型的转换:
大类型转小类型 -----强制转换
小类型转大类型 -----自动转换
instanceof 关键字
用于判断:对象是否属于后面的类型,如果属于则返回true 如果不属于则返回false
class Test{
public static void main(String[] args){
if (a instanceof b) {
System.out.println(true);
// 类型的转换
}else{
System.out.println(false);
}
}
}
JVM虚拟机栈
局部变量存储在栈空间中,这种说法是一种比较粗略的说法,我们通常所说的栈空间,其实指的是虚拟机栈 ,虚拟机栈又包含
方法执行的栈帧。我们正常在代码的执行过程中,都需要方法的调用,每一个方法的调用都是一个栈帧,而栈帧又包含 局部变
量表、操作数栈、方法出口、动态链接。
局部变量表
局部变量表中存储的内容:
- 八大基本数据类型
- 对象的引用
- 方法的返回值 return address
在局部变量表中是一个32位的空间 针对于long类型double类型 都是64位 为了存储这两个类型 开辟2个slot的空间进行存储
- 局部变量表存储的内容 都是有一定生命期限,灵活性比较差 但是运行速度是比堆空间要快
- 堆空间存储的内容是没有生命期限的 灵活性比较好
使用 == 号在比较引用数据类型的时候(new String()) 比较的是变量存储空间的地址,
使用equals 来比较两个字符串是否相同(内容的相同)--建议大家使用