❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:
文章目录
什么是类,对象
因为计算机并不能像我们人这样去理解世界上的东西,所以为了更好的编辑,就抽象出了类和对象。类就是把功能放在一起,然后由一个人去调用这个功能,然后再编辑对应的功能。调用者就是对象的实现者
类和类的实例化
类是一类对象的统称,对象就是这一类具体化的实例
创建类的关键字:class
举例:我们坐年糕的模子就是一个类,而通过这个模子可以做出月饼。在这个例子当中,类就是模具,年糕就是实例化的对象。一个类可以实例化很多对象。举例:
class Person {
public String name;
public int age;
}
public static void main1(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
Person person3 = null;
}
Person 就是一个类,里面的 name 和 int 就是类的成员(字段)。这里的 person1 就是引用,指向的是后面 new 的对象,因为是引用,所以也可以指向 null 。person3 就是指向空(null)引用。person1 person2 person3 就是实例化的对象。
字段的初始化
字段就是类里面的成员。在创建这些成员的时候就可以初始化。例如:
class Person {
public String name = "Lockey";
public int age = 10;
}
类中实现方法
class Person {
public String name;
public int age;
public void print() {
System.out.println("姓名:"+name+" 年龄:"+age);
}
}
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "Lockey";
person1.age = 10;
person1.print();
}
static 静态关键字
static 可以修饰方法,属性。要注意的是:static 修饰的变量通过类名访问。
class Person {
public int a;
public static int count;
}
public static void main(String[] args) {
Person person1 = new Person();
person1.a++;
person1.count++;
System.out.println(person1.a);
System.out.println(Person.count);
Person person2 = new Person();
person2.a++;
person2.count++;
System.out.println(person2.a);
System.out.println(person2.count);
}
如图,count 第二次输出为 2 。这是因为 conut 是被 static 修饰的变量,被 static 修饰之后就变成了常量,就在静态区了。所以每个引用当中调用的 count 没变。所以第二次改变 count 的值的时候,就变成 2 了。当 static 修饰方法的时候,无需再创建对象,直接拿类名调用就行了。举例:
class Person {
public static int count;
public static void change() {
count = 100;
}
}
public static void main(String[] args) {
Person.change();
System.out.println(Person.count);
}
通过类名的调用直接访问 static 修饰的变量。
封装
因为代码实现的软件很复杂,所以就出现了类,但是为了阅读性更高,就出现了封装。就是把一个类的内容实现为只剩一个或几个接口,方便类的调用者访问,这样就降低了代码的复杂程度。
private 封装
private 就是修饰成员变量或方法的关键字,被 private 修饰之后,就不能被类外的调用者访问了。所以就可以配合 public 来实现对外的接口。例如:
class Person {
private String name = "Lockey";
private int age = 10;
public void print() {
System.out.println("名字是:"+name+" 年龄:"+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;
}
}
class test {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getAge());
person.print();
}
}
这里就是利用 private 封装属性,使其只能在类内被访问到。而提供的 public 接口,就让类的调用者在类外就可以访问到了。所以在类外直接调用 print 方法就好了。
setter 和 getter 方法
当我们把字段(name age)设置为 private 属性时,就无法在类外进行初始化了,所以就又出现了新的方法:
class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name1) {
name = name1;
}
public int getAge() {
return age;
}
public void setAge(int age1) {
age = age1;
}
public void print() {
System.out.println("名字是:"+name+" 年龄:"+age);
}
}
public static void main(String[] args) {
Person person = new Person();
person.setName("Lockey");
person.getName();
person.setAge(10);
person.getAge();
person.print();
}
这里就是调用 getter 和 setter 方法,在类外进行访问,因为 private 对 name 和 age 做了封装。
构造方法
方法名和类名是相同的,且构造方法没有返回值。调用完构造方法之后,对象才会产生。
一个对象的产生:
1 为对象分配内存。
2 调用合适的构造方法。
class Person {
public Person() {
System.out.println("Person 不带参数的构造方法");
}
}
public static void main(String[] args) {
Person person = new Person();
}
这里就是实例化类的对象的时候调用的构造方法,因为我们自己写了构造方法,所以这里就调用我i们写过的。就输出了如图所示的内容。需要注意的是:如果没有实现如何的构造方法,编译器会帮我们默认生成有个不带参数的构造方法。也就是说,一个类,至少会有一个构造方法。
除了上面这种构造方法,我们还能写带有参数的构造方法。例如:
class Person {
private String name;
public Person(String name1) {
name = name1;
System.out.println("Person(String) 带一个参数的构造方法");
}
}
public static void main(String[] args) {
Person person = new Person("Lockey");
}
如图:在类初始化的时候,传入参数 “Lockey” 在类实例化的时候就是调用有参数的构造方法。既然能传参数,那是否可以传多个参数呢?答案是可以的。例如:
class Person {
private String name;
private int age;
public Person(String name1, int age1) {
name = name1;
age = age1;
System.out.println("Person(String, int) 带两个参数的构造方法");
}
}
public static void main(String[] args) {
Person person = new Person("Lockey",10);
}
如图,调用的就是传了两个参数的构造方法。通过观察上面的例子,我们可以发现:构造方法也是支持重载的。
this关键字
在类当中,this 表示当前对象的引用(不是当前对象),可以通过 this 来访问类当中的字段和方法。用 this 的话,会降低出现 bug 的概率,并且增加代码的可读性。例如使用 getter 和 setter 的时候,就可以用 this ,能多用 this 就多用 this 。
class Person {
private String name;
private int 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;
}
}
继承
在创建的类当中,是为了给一些抽象的事物提供一些方法。如果一些类、一些食物有共同的属性,那么就可以把这些属性封装为一个类。然后其它事物有这个属性的时候,就去继承这个类。通过 extends 关键字去继承。就比如说:猫和鸟,它们的共同特点都有名字和吃。所以就可以把名字和吃放在一个类里面。然后让它们去继承,降低代码量。把被继承的这个类叫做 Animal 可以发现猫和鸟对于 Animal 都是一种 is-a 的关系。在继承的时候,也会继承父类(Animal)的字段和方法。
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
public class Test4 {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
子类在调用父类的构造方法的时候,要用到 super 关键字。用 super 关键字去把参数传进去。运行结果如下:
bird 就是在 Animal 继承的基础上又扩展出了 fly 方法。
如果把 name 的 public 改成 private 那么子类就访问不到了:
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
protected 关键字
因为把访问权限设置为 private 就会导致子类不能访问。如果是 public 就会导致失去封装的意义。所以就可以使用 protected 。因为对于类的子类和同一个包的其他类来说,protected 修饰的字段是可以访问的。
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
public class Test4 {
public static void main(String[] args) {
System.out.println("保护权限下的访问");
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
java对于字段的方法的四种访问权限
private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问
final关键字
之前学过,用 final 来修饰常数的时候,这个数不能被修改。所以用 final 来修饰类的时候,此时被修饰的类就不能被继承。
final class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
public class Test4 {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
多态
向上转型
在上一篇 包和继承 当中我们写继承的关系的时候,写了这样的代码:
Bird bird = new Bird("圆圆");
当然,我们也可以写成这样:
Bird bird = new Bird("圆圆");
Animal bird2 = bird;
或者下面这样:
Animal bird2 = new Bird("圆圆");
此时 bird2 就是一个父类(Animal)的引用,指向一个子类(Bird)的实例,这就是向上转型。就像可以把:你给笔记本电脑充电了吗?说成:你给电脑充电了吗? 因为笔记本电脑也是电脑。
为什么叫向上转型:因为在程序设计当中,有很多场景。为了表示这种关系,就可以把他们的关系图画出来,父类通常在子类的上方。所以就叫做向上转型,表示往父类的方向转。
方法重写
子类实现了父类的同名方法,并且参数的类型和个数完全相同,就称之为:覆写/重写/覆盖
重写和重载完全不一样.。
普通方法可以重写, static 修饰的静态方法不能重写。
重写中子类的方法的访问权限不能低于父类的方法访问权限。
重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外)。
如果我们把子类当中的权限改成比父类小的话,就会报错。代码如下:
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("这里是动物");
System.out.println(this.name + "正在吃" + food);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
private void eat(String food) {
System.out.println("这里是小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
public class Test4 {
public static void main(String[] args) {
Animal animal1 = new Animal("圆圆");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
重写和重载的区别
理解多态
学了向上转型,动态绑定,方法重写之后,我们就可以使用多态的形式来设计程序了,写一些弗雷可以兼容子类的代码。例如,打印多种形状:
class Shape {
public void draw(){
System.out.println("Shape::draw()");
}
}
class Rect extends Shape{
public void draw() {
System.out.println("♦");
}
}
class Flower extends Shape{
//alt + insert 快捷键输出重写的方法
@Override
public void draw() {
System.out.println("❀");
}
}
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("🔺");
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("●");
}
}
public class Test {
// 打印单个图形
public static void drawShape(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Rect();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
}
这里类的代码是由类的实现者写的,main 函数是由类的调用者写的。当类的调用者在写 drawMap 方法的时候,就会通过父类去看一个调用那个子类的方法。
多态的好处
1.进一步的封装,让类的调用者不知道类的细节
2.降低代码量,避免使用大量的if else
3.可扩展能力更强
super关键字
前面的代码调用的是子类的方法,如果要调用父类的方法就要用到 super 关键字。
1.使用了 super 来调用父类的构造器:
public Bird(String name) {
super(name);
}
2.使用 super 关键字来调用父类的普通方法:
class Bird extends Animal {
public Bird(String name) {
super(name);
}
@Override
public void eat(String food) {
//在这里让子类调用父类的接口
super.eat(food);
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
public class test5 {
public static void main(String[] args) {
Bird bird = new Bird("xxn");
bird.eat("ee");
}
}
super和this的区别
抽象类
就像这个代码:
class Shape {
public void draw(){
System.out.println("Shape::draw()");
}
}
class Rect extends Shape{
public void draw() {
System.out.println("♦");
}
}
class Flower extends Shape{
@Override
public void draw() {
System.out.println("❀");
}
}
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("🔺");
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("●");
}
}
public class Test {
public static void main(String[] args) {
Rect rect = new Rect();
Flower flower = new Flower();
Triangle triangle = new Triangle();
Shape[] shapes = {triangle,rect,triangle,rect,flower,new Cycle()};
for (Shape s: shapes) {
s.draw();
}
}
}
在这个打印图形的例子当中,可以看到父类的 shape 方法并没有实际作用,主要的工作都由子类完成了,像这些没有实际工作的方法,我们就可以把它设计成一个抽象方法,包含抽象方法的类叫做抽象类:
abstract class Shape{
public abstract void draw();
}
在 draw 方法前面加上 abstract 关键字就变成了抽象方法,但是包含抽象方法的类,必须用 abstract 修饰。抽象方法不用具体实现,抽象类就是Shape,然后Shape下面有很多的具体实现的子类
使用抽象类的注意事项
1.抽象类不能直接实例化
public static void main(String[] args) {
Shape shape = new Shape();
}
2.抽象方法是不能用 private 修饰的
abstract class Shape{
private abstract void draw();
}
3.抽象类中可以包含其他的非抽象方法,也可以包含字段,这里的非抽象方法和普通方法是一样的,可以被重写,也可以被子类直接调用,但是一个普通类要继承抽象类,那么必须重写抽象类当中的所有抽象方法。
abstract class Shape{
public void func() {
System.out.println("func");
}
public abstract void draw();、
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("♦"+a);
super.func();
}
}
public class Test {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Shape shape = new Rect();
shape.func();
}
}
抽象类的作用
抽象类存在的最大意义就是为了被继承,抽象类本身并不能被实例化,要想使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。
接口
接口是抽象类的更进一步,抽象类中还可以包含非抽象方法和字段。而接口中包含的方法都是抽象方法,字段只能包含静态常量。
在上面的打印图像代码当中,父类 shape 并没有包含别的抽象方法,所以就可以设计成一个接口。代码示例:
interface IShape {
void draw();
}
class Cycle implements IShape {
@Override
public void draw() {
System.out.println("○");
}
}
public class Test5 {
public static void main(String[] args) {
IShape shape = new Cycle();
shape.draw();
}
}
这里应该注意的是:
使用接口的时候,要用 interface 定义。
接口中的方法一定是 public ,因此可以省略 public 。
接口中的方法一定是抽象方法,因此可以省略 abstract 。
Cycle 使用 implements 继承接口,此时表达的含义不再是“扩展”,而是“实现”。
静态常量代码当中的 public static final 关键字都可以省略
接口不能单独被实例化。
接口当中只能包含抽象方法,对于字段来说,接口中只能包含静态常量:
interface IShape {
void draw();
public static final int num = 10;
}
实现多个接口
有些时候我们需要让一个类同时继承多个父类,但是 Java 实现不了多继承。不过可以通过同时实现多个接口来达到多继承类似的效果。通过类来表示一组动物(通过接口来调用就不用关心引用是谁了):
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
interface IFlying {
void fly();
}
interface IRunning{
void run();
}
interface ISwimming{
void swimming();
}
class Bird extends Animal implements IFlying{
public Bird(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(this.name+"正在飞");
}
}
class Frog extends Animal implements IRunning,ISwimming{
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在跑");
}
@Override
public void swimming() {
System.out.println(this.name+"在游泳");
}
}
class Duck extends Animal implements IRunning,ISwimming,IFlying{
public Duck(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(this.name+"正在飞");
}
@Override
public void run() {
System.out.println((this.name+"正在跑"));
}
@Override
public void swimming() {
System.out.println(this.name+"在游泳");
}
}
class Roobot implements IRunning{
@Override
public void run() {
System.out.println("机器人在跑");
}
}
public class Test4 {
public static void runFunc(IRunning iRunning){
iRunning.run();
}
public static void swimmingFunc(ISwimming iSwimming){
iSwimming.swimming();
}
public static void flyingFunc(IFlying iFlying){
iFlying.fly();
}
public static void main(String[] args) {
runFunc(new Duck("鸭子"));
runFunc(new Frog("青蛙"));
runFunc(new Roobot());
}
}
接口间的继承
接口也可以继承一个接口(也叫扩展),达到复用代码的效果。也是使用 extends 关键字:
interface IA1 {
void funcA();
}
interface IB1 extends IA1{
void funcB();
}
//因为要拓展接口的功能 所以有 B 和 A 所以这俩的方法都有重写
class C implements IB1{
@Override
public void funcB() {
}
@Override
public void funcA() {
}
}
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章