正文:
1.什么是类?什么是对象?
我来举个例子:
比如现在要你去雕刻一个雕像,那我会跟你说要雕刻些什么,比如有眼睛,鼻子,嘴巴,高矮胖瘦等等,而我说的这些内容就是一个类,你根据这些内容所雕刻出来的东西就叫做对象,由此我们可以得出:
类是来描述一个对象的;对象是一个真实存在的实体。
类是抽象的,不存在;对象是具象的,它真实存在。
2.面向对象和面向过程:
有两种编程,一种是面向过程的编程,一种是面向对象的编程,我们所学的Java恰好就是一门面向对象的计算机语言。
顾名思义,面向对象的编程关注的是对象,面向过程的编程关注的是过程。
以怎么把大象放在冰箱里举例:
如果是面向过程,我们需要关注完成这件事情的所有步骤,而如果是面向对象的话,就根本不用care过程,我们只需要找对象->创建对象->使用对象,任务的完成靠的是对象与对象之间协作完成,我们不需要关注他们是怎么完成任务的,这些不是我们所需要知道的。
面向过程:
面向对象:
3.类(自定义类型):
语法:
注:建议一个文件中只放一个类,被public修饰的类的名字要与文件名相同。
简单写一个类:
Dog类:
public class Dog {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃狗粮....");
}
public void bark() {
System.out.println(this.name + "正在汪汪叫....");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();//实例化
}
}
成员变量会有一个默认值,可以不初始化;
默认值规则:引用类型为null,基础类型是对应的“ 0 ”。
通过对象的引用访问成员变量和方法。
可以发现编译器并没有报错:
实例化的对象在内存中的储存形式:
可以通过关键字new实例化多个对象:
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.name = "小黑";
dog1.age = 2;
Dog dog2 = new Dog();
dog2.name = "大黄";
dog2.age = 6;
Dog dog3 = new Dog();
dog3.name = "小白";
dog3.age = 1;
}
}
构造方法:
其实在实例化对象的时候Dog()就是一个方法,这种方法在Java中被叫作构造方法,是一种特殊类型的方法,在创建类的实例对象时被调用。它的主要作用是初始化对象的状态,包括设置对象的初始值和执行必要的操作,以确保对象在创建后处于一个合理的状态。
一个对象的生成至少有两步很重要:
1. 为对象分配内存;
2. 调用合适的构造方法。
构造方法的语法:
类名 (形参列表) {
方法体;
}
实例化对象一定会调用构造方法,但我们并没有在类中写这一方法,所以由此可知在我们没有写其他构造方法的时候,Java会默认给我们一个没有参数的构造方法。这个默认的构造方法的代码如下:
public Dog() {
}
我们写一个构造方法并在其中调用Dog():
发现编译器报错了,由此可得出结论:当你没有写任何构造方法时,Java会帮你提供一个默认的不带参数的构造方法,但是一旦你有了其他的构造方法,Java就不会再给你提供不带参数的构造方法了!!!(救急不救穷)
public Dog(String name, int age) {
this.name = name;
this.age = age;
Dog();
}
构造方法可以发生重载,例:
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
那我们想在一个构造方法中调用另一个构造方法又该怎么做,这就涉及到了this关键字了。
this:
观察以下代码的运行的结果:
可以发现我们并没有修改到对象的成员变量;
原因:
形参名和成员变量名相同,变量都是局部变量优先,形参自己给自己赋值,并没有修改到对象当中的成员变量。
解决方案:
在成员变量名的前面加上this,this代表当前对象的引用。
public class Dog {
public String name;
public int age;
public void setName(String name, int age) {
name = name;
age = age;
}
public void eat() {
System.out.println(this.name + "正在吃狗粮....");
}
public void bark() {
System.out.println(this.name + "正在汪汪叫....");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("小黑",4);
System.out.println(dog.name);
System.out.println(dog.age);
}
}
this 的用法有三种:
1. 可以通过this访问当前对象的成员变量;
this.name = name;
this.age = age;
2. 可以通过this访问当前对象的非静态的成员方法;
this.eat();
3. 可以通过this访问当前对象的其他构造方法。
public Dog() {
this("小黑", 3);
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
注:this来调用当前对象的其他构造方法时必须在第一行。
this不能在静态方法中使用,原因:静态方法不依赖于对象。
对于什么是静态,我们接下来介绍。
static:
被static修饰的成员不属于对象,静态的成员只有一份,且存在方法区当中,具体我来画图表示一下:
由下图知,我们并没有在实例化dog对象的时候对其weight进行初始化,由此可知,只要我们不改变weight的值,以后它创建的对象都会是45。
访问静态变量的形式有两种:
1. 通过对象的引用访问(dog.weight = 45);
2. 通过类名访问(Dog.weight = 45);
注:虽然两种访问形式编译器都不会报错,但是第二种访问形式更合理。
原因:静态的成员变量也叫作类变量,也就是说这个变量是类的,不是对象的,它的存在不依赖于对象。
在非静态方法中可以调用静态方法,但静态方法中不能够直接调用非静态方法,且静态方法中不能使用this关键字。
原因:静态方法是类方法,不依赖于对象,可以直接通过类名进行访问,但是非静态方法依赖于对象,需要通过对象的引用访问。
static只能在方法外使用,因为static定义的变量是类变量,它存在于方法区,而在方法内定义的变量是存在于栈中的,如果在方法内使用static会有矛盾。
方法或成员变量的调用就两种情况:
1. 通过对象的引用调用;
2. 通过类名调用。(在静态方法中不能直接使用任何非静态的成员变量和成员方法。要想使用,要通过new对象,然后通过对象的引用访问)
静态成员变量的初始化:
1. 就地初始化;
public String name = "小黑";
2. 通过get和set初始化;
public class Cat {
public static String name;
public static int age;
public static double weight;
public static String getName() {
return name;
}
public void setName(String name) {
Cat.name = name;
}
public void setAge(int age) {
Cat.age = age;
}
public static void setWeight(double weight) {
Cat.weight = weight;
}
public static int getAge() {
return age;
}
public static double getWeight() {
return weight;
}
}
补充:编译器一般都会带有自动生成get和set的功能,idea的具体步骤如下:
3. 构造方法:(这种方法一般很少用)
public class Cat {
public static String name;
public static int age;
public static double weight;
public Cat(String name, int age, double weight) {
Cat.name = name;
Cat.age = age;
Cat.weight = weight;
}
}
4. 代码块:
代码块分为三种:
一. 静态代码块:定义在类当中,一般用来初始化静态成员变量。
二. 非静态/示例/构造代码块。
三. 同步代码块。
我们接下来分别讲解一下这几种代码块:
静态代码块:在加载类的时候被执行。如果出现多个静态代码块,都是静态的情况下,和定义顺序有管。
解答:静态代码块是在加载类的时候被执行的,只要你new一个对象,它就会被调用,但是只有在第一次new对象的时候才会执行,也就是它只会执行一次,由此可知这个类只会加载一次。
实例代码块:
格式:
{
//代码
}
实例化代码块在每次实例化对象的时候都会被执行,根据下面的图我们也可以看出静态代码块的执行在实例代码块之前,并且和定义顺序无关。
在new对象的时候,先执行静态代码块,再执行实例代码块,最后执行构造方法,且只有在第一次实例化对象的时候才会执行。(与定义顺序无关)
成员变量的打印:
格式:
public 类型 toString() {
return 字符串;
}
这里涉及到了动态绑定,后面会详细讲解,我们先见识一下。
继承:
观察以下代码发现Cat和Dog有一些相同的类,如果相同的成员变量有很多的话我们去一个一个重复敲会很浪费时间,这时就引进了继承这个概念。
Cat类:
public class Cat {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃猫粮....");
}
public void mimi() {
System.out.println(this.name + "正在咪咪叫....");
}
}
Dog类:
public class Dog {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃狗粮....");
}
public void bark() {
System.out.println(this.name + "正在汪汪叫....");
}
}
继承允许一个类(称为子类或派生类)基于另一个类(称为父类、基类或超类)来定义自己的特性和行为。通过继承,子类可以获得父类的属性和方法,并可以在此基础上进行扩展或修改,从而实现代码的重用和扩展。
那我们利用继承来简化上面的代码结果如下:
Cat类:
public class Cat extends Animal{
public void mimi() {
System.out.println(this.name + "正在咪咪叫....");
}
}
Dog类:
public class Dog extends Animal{
public void bark() {
System.out.println(this.name + "正在汪汪叫....");
}
}
Animal类:
public class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃饭....");
}
}
那如果父类中和子类有相同的变量名怎么办?
答案就是他会优先访问子类的成员变量。
Dog类:
public class Dog extends Animal{
public String name = "小黑";
public void bark() {
System.out.println(this.name + "正在汪汪叫....");
}
}
Animal类:
public class Animal {
public String name = "小白";
public int age;
public void eat() {
System.out.println(this.name + "正在吃饭....");
}
}
Test类:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
}
}
运行结果:
那我就是想引用父类的变量怎么办呢?
这时我们可以用一个关键字super来对成员变量进行引用。
Dog类:
public class Dog extends Animal{
public String name = "小黑";
public void bark() {
System.out.println(super.name + "正在汪汪叫....");
}
}
Animal类:
public class Animal {
public String name = "小白";
public int age;
public void eat() {
System.out.println(this.name + "正在吃饭....");
}
}
Test类:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
}
}
运行结果:
super只能指代当前类的父类,不能指代父类的父类甚至继续往上指。
super就是个关键字,用来提高代码的可读性的!!
记住下面这张图:
由于继承和多态放在一起讲会更加方便,所以继承的详细讲解会在下一篇博客呈现。。。
访问修饰限定符:
在定义成员变量和方法的时候我们总会发现在前面会加一些修饰符,例如public,private, protected等等等等。
这些都叫访问修饰限定符。(管当前修饰的字段或方法的访问权限)
如果字段或方法前面不加修饰符,那么默认的是default。
有人可能会疑惑为什么要有这么个东西。
这里涉及到了封装。
封装:
我们简单举个例子先简单了解一下:
例如手机,我们是看不到它的内部实现细节的,只会留一些接口给你使用。
从语言上来说,想达到这种效果只能对类进行封装,把细节隐藏起来,提供一些你公开可以访问的内容就可以了。这也正面体现了面向对象。
我们再来了解一下什么是包?
包:
包的本质其实就是文件夹。
package demo2的作用就是声明当前类是在哪个包底下。