🎇个人主页:Ice_Sugar_7
🎇所属专栏:快来卷Java啦
🎇欢迎点赞收藏加关注哦!
类和对象2
- 🍉匿名对象
- 🍉关键字static
- 🍌static修饰成员变量
- 🍌static修饰成员方法
- 🍌static成员变量初始化
- 🍉代码块
- 🍌普通代码块
- 🍌构造代码块
- 🍌静态块
- 🍉继承
- 🍌关键字extends
- 🍌继承关系中的访问
- 🍌关键字super
- 🍌子类的构造方法
- 🍌再谈初始化
- 🍌继承关系中的访问权限
- 🍌继承方式及关键字final
- 🍉组合
- 🍉写在最后
🍉匿名对象
特点:使用new来创建对象,但是不声明对象的引用变量
缺点:只能使用一次,不能重复使用
System.out.println(new Student("sugar","male",21));
而数组也是一个对象,所以我们也可以采用匿名对象来打印数组:
for (int i:array1) {
System.out.print(i + " ");
}
System.out.println(" ");
for(int i:new int[]{2,3,4,5,6}) {
System.out.print(i + " ");
}
🍉关键字static
之前在Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量)
而被static
修饰的成员,称为静态成员
,也可以称为类成员
,它不属于某个具体的对象,而是所有对象共享的
(注意:static不可以修饰局部变量,因为局部变量不属于类)
🍌static修饰成员变量
特性:
●存储在方法区当中,不存储在某个对象的空间中。不属于某个具体的对象,是类的属性,所有对象共享的
●既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
●生命周期伴随类的一生(就是随类的加载而创建,随类的卸载而销毁)
🍌static修饰成员方法
静态成员方法也不是某个对象所特有的
可以通过对象调用,也可以通过类名.静态方法名(...)
方式调用,更推荐使用后者
注意:
静态方法中不能调用非静态成员变量、非静态成员方法
因为调用非静态成员变量需要使用this来访问;调用非静态成员方法需要传一个this引用,而静态方法中不含this引用
🍌static成员变量初始化
静态成员变量的初始化分为两种:就地初始化
和静态代码块初始化
那什么是静态代码块呢?往下看~
🍉代码块
使用 {}
定义的一段代码称为代码块
。根据代码块定义的位置以及关键字,可以分为以下四种:
●普通代码块
●构造块
●静态块
●同步代码块(后续在多线程部分讲解)
🍌普通代码块
定义在方法中的代码块(这种用法较少见)
public class Main{
public static void main(String[] args) {
{ //直接使用{}定义,普通方法块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}
🍌构造代码块
构造代码块,又叫实例代码块。位于成员方法外、类里面,不用加修饰符,一般用于初始化成员变量,所以它只有在创建对象时才会执行
class Student {
public String name;
public String sex;
public int age;
//构造代码块
{
this.name = "zhangsan";
this.sex = "male";
this.age = 30;
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
System.out.println(student1);
}
}
🍌静态块
使用static
修饰的代码块称为静态代码块,也是位于成员方法外、类里面,一般用于初始化静态成员变量
比如对于学生类,有一个classroom的静态成员,那就使用静态块进行初始化
public static String classroom; //上课的教室
static {
classroom = "C-501";
}
注意:
●不管生成多少个对象,每个静态块都只会执行一次
●先执行静态块,然后执行构造块,再执行对应的构造方法
●如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行
🍉继承
面向对象思想中提出了继承的概念,用来提取不同类的共性
,实现代码复用
继承允许在保持原有类特性的基础上进行扩展
,增加新功能
,这样产生新的类,称为派生类
例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用
在上图中,Dog类和Cat类都继承了Animal类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可
🍌关键字extends
Java中表示类之间的继承关系,需要用到关键字extends
//Animal.java
public class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name+"在吃饭");
}
public void sleep() {
System.out.println(name+"在睡觉");
}
}
//Cat.java
public class Cat extends Animal{
void Catch() {
System.out.println(name+"在抓老鼠");
}
}
//Dog.java
public class Dog extends Animal{
void bark() {
System.out.println(name+"在汪汪叫");
}
}
子类会将父类中的成员变量
、成员方法
继承到子类中
注意:被private修饰的成员变量,可以被子类继承,但是不能直接在子类中访问,要访问的话,需要在父类中写一个get方法
间接访问
🍌继承关系中的访问
一、访问成员变量
①子类和父类不存在同名的成员变量
直接访问父类从父类继承下来的成员变量
②子类和父类存在同名的成员变量
优先访问子类的成员变量
不管是哪种情况,访问成员变量都遵循就近原则
:自己有就优先用自己的;如果没有再从父类中找
二、访问成员方法
①成员方法名字不同
成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,优先访问自己的,自己没有时,再到父类中找,如果父类中也没有则报错
public class Base {
public void methodA(){
System.out.println("Base中的methodA()方法");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
}
}
②成员方法名字相同
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用的方法所传递的参数选择合适的方法访问,如果没有则报错
在了解这些访问顺序之后,有这样一个问题:如果子类中存在与父类相同的成员时,那该如何在子类中访问从父类中继承下来的同名的成员呢?
答案是:使用super关键字
🍌关键字super
使用super时需要注意它的使用场景:
●只能在非静态方法
中使用
●在子类方法
中,要访问父类的成员变量和方法的时候用
(当然,super还有其他用法,后面会讲)
public class Animal {
public String name;
public int age;
public void method() {
System.out.println("这是父类的method方法");
}
}
public class Dog extends Animal{
public String name;
public int age;
public String color;
@Override
public void method() { //和父类的method方法构成重写
System.out.println("这是子类的method方法");
}
public void method1() {
method();
super.method(); //用super访问父类的method方法
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.method1();
}
}
🍌子类的构造方法
构造子类对象时,需要先调用父类的构造方法,然后再执行子类的构造方法
子类构造方法中默认会调用父类无参的构造方法,即super()
。你没有写的时候编译器会帮你自动添加上去
所以,如果你在父类中写了含参的构造方法,那你在子代构造方法里面就要显式调用super了,因为此时父类中已经没有编译器默认生成的无参构造函数
而且super一定要放在第一句,否则会报错
public class Animal {
public String name;
public int age;
Animal(String name,int age) {
this.name = name;
this.age = age;
}
}
public class Dog extends Animal{
public String color;
Dog() {
super("Zhangsan",15); //显式调用super
this.color = "white";
}
}
最后来总结一下super和this的异同之处:
同:
①只能在类的非静态方法
中使用,用来访问非静态成员方法和字段;
②在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
异:
●this是当前对象
的引用;super相当于是子类对象中从父类继承下来部分成员
的引用
●在非静态成员方法中,this用来访问本类
的方法和属性;super用来访问父类继承下来
的方法和属性
●在构造方法中:this(…)用于调用本类构造方法
,super(…)用于调用父类构造方法
,两种调用不能同时在构造方法中出现
●构造方法中一定会存在super(…)的调用,你没有写编译器也会添加;但是this(…)你不写就没有
🍌再谈初始化
再回顾一下上面所说的代码块的执行顺序
静态代码块->实例代码块->构造方法
结合继承关系,那么顺序就是:
父类静态代码块->子类静态代码块->父类实例代码块->父类构造方法->子类的实例代码块->子类构造方法
注意:第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
🍌继承关系中的访问权限
前面我们认识了四个访问限定符,那么父类中不同访问权限的成员,在子类中的可见性如何呢?
public和private就不用多说了,它们代表两个极端。父类中public修饰的成员,同个包和不同包中的子类都能访问;而private则都不行
要重点来说的是protected,展示一下不同包中的子类和非子类权限的区别:
//包1
public class Base {
public int a;
protected int b;
}
//包2
//不同包中的子类
public class Derived extends Base{
public void func() {
super.a = 1;
super.b = 666; //正常访问
}
}
//包3
public class A1 {
public static void main(String[] args) {
Base base = new Base();
base.a = 1;
base.b = 1; //报错,因为b在A1中不可见
}
}
我们了解访问权限是为了能让类尽量做到“封装”,即隐藏内部实现细节,只暴露出必要的信息给类的调用者
因此我们在使用时应尽可能使用比较严格的访问权限。比如:如果一个方法能用 private,就尽量不要用 public
(当然,还有一种简单粗暴的做法:将所有的成员变量都设为 private
,将所有的成员方法设为 public
。但是这种方式属于滥用权限,不太推荐)
总而言之,写代码的时候要思考这个类提供的成员变量、方法到底给“谁”用的(是类内部自己用,还是类的调用者使用,还是子类使用)
🍌继承方式及关键字final
Java支持单继承
、多层继承
、不同类继承同一个类
这三种继承方式,如图:
在实际项目中,我们不希望类之间的继承层次太复杂,一般我们不希望出现超过三层的继承关系,如果继承层次太多,就需要考虑对代码进行重构了
如果想从语法上限制继承,可以使用关键字final
final可以用来修饰变量、成员方法以及类
①修饰局部变量或成员变量,表示常量,不能再修改了
final int a = 10;
a = 20; // 编译出错
②修饰类,表示这个类不能被继承
修饰方法,表示该方法不能被重写
🍉组合
和继承类似,组合也是一种表达类之间关系的方式,它也可以实现代码复用。但是组合并没有涉及到特殊的语法(比如 extends 这样的关键字),它是将一个类的对象作为另外一个类的成员变量
关于二者的区别,通俗来说
●继承表示的对象之间是is-a
的关系,比如:狗是一种动物(dog is an animal)
●而组合表示对象之间是has-a
的关系,比如:汽车有多种零件,汽车和轮胎、发动机、方向盘、车载系统等的关系就是组合,因为汽车是由这些部件组成的
public class Engine {
//...
}
public class Tire {
//...
}
public class car {
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
}
// 奔驰是汽车
public class BenZ extends car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}
●组合相较于继承,整体类与局部类彼此相对独立,不会破坏封装
●而对于继承,由于子类与父类之间紧密耦合,子类依赖于父类的实现,缺乏独立性,所以继承会破坏封装
简而言之就是:能用组合就尽量使用组合
继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。——《Java编程思想》
只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在is-a关系的时候,类B才应该继承类A。——《Effective Java》
🍉写在最后
以上就是本篇文章的全部内容,如果你觉得本文对你有所帮助的话,那不妨点个小小的赞哦!(比心)