目录
一、初步认识类和对象
1.C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题

面向对象是思考问题的一种思考方式,是一种思想。比如:概念与实例。理论与实践。名和实等等。它的好处是将复杂的事情变简单了,只要面对一个对象就行。面向对象设计把握一个重要的经验:谁拥有数据,谁对外提供操作这些数据(私有)的方法(被动的一方是数据的拥有者,主动的一方是执行者)。开发时:找对象,建对象,用对象,并维护对象之间的关系
// 创建类
class <class_name>{
field;//成员属性
method;//成员方法
}
// 实例化对象
<class_name> <对象名> = new <class_name>();
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字 new 来创建一个对象。
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象(稍后会说)
(4)同一个类可以创建多个实例
class Person {
public int age;//成员属性 实例变量
public String name;
public String sex;
public void eat() {//成员方法
System.out.println("吃饭!");
}
public void sleep() {
System.out.println("睡觉!");
}
}
public class Main{
public static void main(String[] args) {
Person person = new Person();//通过new实例化对象
Person person2 = new Person();
//如果改为Person person2 = person;那么它的意思是person2这个引用指向person这个引用所指向的对象,并不是“引用指向引用”
person.eat();//成员方法调用需要通过对象的引用调用
person.sleep();
}
}
编译并运行该代码,输出如下:
吃饭!
睡觉!
内存布局如图所示:
看这张图,我们感觉好像引用都在栈上,其实引用不一定在栈上,如:
class Person {
public int age;
public String name;
public String sex;
}
public class Test{
Person person = new Person();
public static void main(String[] args) {
Test test = new Test();
}
}
其内存布局是这样的:
二、类的成员
类的成员可以包含以下:字段、方法、代码块、内部类和接口等。但是本笔记重点总结前3种
1.字段
- 定义在类中, 但是方法外部定义的变量. 这样的变量我们称为 "字段" 或 "属性" 或 "成员变量"(三种称呼都可以, 一般不会严格区分),成员变量又分为普通成员变量(普通成员变量都属于对象)和静态成员变量(类变量),以下类中的是普通成员,笔记的后面会遇到静态成员变量。
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
class Person {
public String name; // 字段/属性/成员变量
public int age;
}
class Test {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
}
}
编译并运行该代码,输出如下:
null
0
(1)使用 . 访问对象的字段,"访问" 既包含读, 也包含写
class Person {
public String name = "张三";
public int age = 18;
}
class Test {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
}
}
我们并不建议这么初始化,因为当我们new多个对象时,不见得每个对象都叫张三,都是18岁。我们可以这样初始化:
class Person {
public String name;
public int age;
}
class Test {
public static void main(String[] args) {
Person person = new Person();
person.name = "张三";
person.age = 18;
System.out.println(person.name);
System.out.println(person.age);
}
}
编译并运行该代码,输出如下:
张三
18
2.方法
用于描述一个对象的行为
方法又分为普通方法和静态方法,以下类中的是普通方法,笔记的后面会遇到静态方法
使用 . 访问对象的方法
class Person {
public int age;
public String name;
public void show() {
System.out.println("我叫" + name + ", 今年" + age + "岁");
}
}
class Test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.age = 18;
person1.name = "张三";
person2.age = 19;
person2.name = "李四";
person1.show();
person2.show();
}
}
编译运行该代码,输出如下:
我叫张三,今年18岁
我叫李四,今年19岁
3.static 关键字
它可以修饰属性,修饰方法,修饰代码块,修饰类
(1)修饰属性(Java静态属性和类相关, 和具体的实例无关. 换句话说, 同一个类的不同实例共用同一个静态属性)
class Test{
public int a;//普通成员变量 实例变量 存放在对象中
public static int count;//类变量也叫静态成员变量
public final SIZE;//被final修饰的叫常量,也属于对象。 被final修饰,后续不可更改
public static final int COUNT = 99;//静态的常量,属于类本身,只有一份 被final修饰,后续不可更改
}
public class Main{
public static void main(String[] args) {
Test t1 = new Test();
t1.a++;
Test.count++;
System.out.println(t1.a);
System.out.println(Test.count);
System.out.println("---------------");
Test t2 = new Test();
t2.a++;
Test.count++;
System.out.println(t2.a);
System.out.println(Test.count);
}
}
编译并运行该代码,输出如下:
1
1
---------------
1
2
注意:count被static所修饰,所有类共享。且不属于对象,访问方式为:类名 . 属性
内存解析如下:
(2)修饰方法
class TestDemo{
public int a;
public static int count;
public static void change() {
count = 100;
//a = 10; error 不可以访问非静态数据成员
}
}
public class Main{
public static void main(String[] args) {
TestDemo.change();//无需创建实例对象 就可以调用
System.out.println(TestDemo.count);
}
}
编译并运行该代码,输出如下:
100
三、封装
1.什么是封装
2.private实现封装
class Person {
private String name = "张三";
private int age = 18;
public void show() {
System.out.println("我叫" + name + ", 今年" + age + "岁");
}
}
class Test {
public static void main(String[] args) {
Person person = new Person();
//person.name = "张三";//error
person.show();
}
}
3.getter和setter方法
- 当set方法的形参名字和类中的成员属性的名字一样的时候,如果不使用this, 相当于自赋值. this 表示当前实例的引用
- 不是所有的字段都一定要提供 setter / getter 方法, 而是要根据实际情况决定提供哪种方法
- 在 IDEA 中可以使用 alt + insert (或者 alt + F12) 快速生成 setter / getter 方法. 在 VSCode 中可以使用鼠标右键菜单 -> 源代码操作中自动生成 setter / getter 方法
class Person {
private String name;//实例成员变量
private int age;
public void setName(String name){
//name = name;//不能这样写,此时3个name都是同一个name,因为局部变量优先
this.name = name;//this引用,表示调用该方法的对象
}
public String getName(){
return name;
}
public void show(){
System.out.println("name: "+name+" age: "+age);
}
}
public static void main(String[] args) {
Person person = new Person();
person.setName("xiaoxiao");
String name = person.getName();
System.out.println(name);
person.show();
}
编译并运行该代码,输出如下:
xiaoxiao
name: xiaoxiao age: 0
四、构造方法
class Person {
private String name;//实例成员变量
private int age;
private String sex;
public Person() { //默认构造函数 构造对象 此处的public不能修改为private,一旦这样修改在类外就无法实例化对象了,不过在类内可以
this.name = "caocao";
this.age = 10;
this.sex = "男";
}
public Person(String name,int age,String sex) { //带有3个参数的构造函数 两个构造方法之间构成重载
this.name = name;
this.age = age;
this.sex = sex;
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main{
public static void main(String[] args) {
Person p1 = new Person();//调用不带参数的构造函数 如果程序没有提供会调用不带参数的构造函数
p1.show();
Person p2 = new Person("zhangfei",80,"男");//调用带有3个参数的构造函数
p2.show();
}
}
编译并运行该代码,输出如下:
name: caocao age: 10 sex: 男name: zhangfei age: 80 sex: 男
class Person{
private String name;
public Person(){
//this();//error 调用对象的其他构造方法,构成了无限递归
this("xiaoxiao");//调用带有一个参数的构造方法 必须放在第一行
System.out.println("Person()-->不带参数的构造方法");
}
public Person(String name){
this.name = name;
System.out.println("Person(String)-->带1个参数的构造方法");
}
public void show() {
System.out.println("name: "+name);
}
}
public class Test{
public static void main(String[] args){
Person person = new Person();
person.show();
System.out.println(person);
}
}
编译并运行该代码,输出如下:
xiaoxiao
Person(String)-->带1个参数的构造方法
Person()-->不带参数的构造方法
五、代码块
1.字段的初始化方式有: (1)就地初始化 (2)使用构造方法初始化 (3)使用代码块初始化。前两种方式前面已经总结过了, 接下来我们介绍第三种方式, 使用代码块初始化
2.根据代码块定义的位置以及关键字,又可分为四种:本地代码块(普通代码块)、静态代码块、构造代码块、同步代码块
(1)本地代码块:定义在方法中的代码块
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);
}
}
编译并运行该代码,输出如下:
x1 = 10
x2 = 100
这种用法比较少见
(2)构造代码块
class Person{
private String name;//实例成员变量
private int age;
private String sex;
public Person() {
System.out.println("I am Person init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
p1.show();
}
}
编译并运行该代码,输出如下:
I am instance init()!I am Person init()!name: bit age: 12 sex: man
class Person{
private String name;//实例成员变量
private int age;
private String sex;
private static int count = 0;//静态成员变量 由类共享数据 方法区
public Person(){
System.out.println("I am Person init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
//静态代码块
static {
count = 10;//只能访问静态数据成员
System.out.println("I am static init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();//该静态代码块不会被执行
}
}
编译并运行该代码,输出如下:
I am static init()!I am instance init()!
I am Person init()!
还有一个点:如果都是静态成员变量,那么其执行的顺序要看定义的顺序
class Person1{
public static int count1 = 10;
static{
count1 = 99;
}
}
class Person2{
static{
count2 = 99;
}
public static int count2 = 10;
}
class Person3{
static{
count3 = 99;
}
public static int count3;
}
class Person4{
public static int count4;
static{
count4 = 99;
}
}
public class Test{
public static void main(String[] args) {
System.out.println(Person1.count1);
System.out.println(Person2.count2);
System.out.println(Person3.count3);
System.out.println(Person4.count4);
}
}
编译并运行该代码,输出如下:
99
10
99
99
由这个结果可以看到有个例外,那就是当count没有被初始化时,默认值就是99
六、内部类
1.内部类的概念和分类:
定义在类内部的类。分为4种:①本地内部类②静态内部类③实例内部类④匿名内部类
2.本地内部类
本地内部类:定义在方法中的类
public class TestDemo {
public void func(){
class Test{
public int a;
}
//本地内部类只能在当前方法中使用
}
}
本地内部类用得很少,因为它只能在当前方法中使用
3.实例内部类
实例内部类:定义在类内部的一个普通的类
注意:
(1)在实例内部类中不能定义一个静态的成员变量,如果非要定义,只能定义1个,而且它是静态常量
(2)实例化内部类的对象:
外部类名.内部类名 变量 = 外部类对象的引用.new 内部类();
(3)最终生成的字节码文件
(4)实例内部类当中包含两个this,一个是外部类的this,一个是内部类的this
interface A{
}
class OuterClass{
public int data1 = 1;
private int data2 = 2;
public static int data3 = 3;
//实例内部类,我们可以把它当成外部类的一个普通实例的成员
class InnerClass implements A{//内部类实现接口(只要是类肯定会有类的特性)
public int data1 = 999;
public int data4 = 4;
private int data5 = 5;
//private static int data6 = 6;//error data6是属于类的,但是InnerClass的调用需要依赖于对象
private static final int DATA6 = 6;
public InnerClass(){
System.out.println("不带参数的内部类的构造方法");
}
public void test(){
System.out.println(OuterClass.this.data1);//外部类的this
System.out.println(this.data1);//内部类的this
System.out.println(DATA6);
System.out.println("InnerClass::test()");
}
}
public void func1(){
System.out.println("OuterClass::func1()");
}
}
public class TestDemo extends OuterClass.InnerClass{//内部类的继承(只要是类肯定会有类的特性)
public TestDemo(OuterClass out){
out.super();
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.test();
}
}
编译并运行该代码,输出如下:
(5)一般什么时候用到内部类?
//之前我们写链表的时候是这样写的
class Node{
}
class MyLinkedList{
}
//其实可以这样写(“某个东西是由什么什么组成”的关系可以用到内部类,如:链表由一个一个的节点组成)
class MyLinkedList{
class Node{
}
}
4.静态内部类
class OuterClass2{
public int data1 = 1;
private int data2 = 2;
public static int data3 = 3;
//静态内部类
static class InnerClass{
public int data4 = 4;
private int data5 = 5;
public static int data6 = 6;
//写法二:
public OuterClass out = new OuterClass();
public void test(){
//System.out.println(data1);//error data1、data2的调用时依赖于外部类的引用来调用的,并且访问data2还要提供getter和setter方法
//System.out.println(data2);//error
System.out.println(new OuterClass2().data1);//写法一
System.out.println(out.data1);//写法二
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
System.out.println("InnerClass::test()");
}
}
}
public class TestDemo2 {
public static void main(String[] args) {
OuterClass2.InnerClass innerClass = new OuterClass2.InnerClass();
innerClass.test();
}
}
5.匿名内部类
匿名内部类:顾名思义,就是没有名字的内部类
class Test{
public void test(){
System.out.println("test()haha!");
}
}
public class TestDemo2 {
public static void main(String[] args) {
new Test(){
@Override
public void test(){
System.out.println("我是重写的test方法!");
}
}.test();
}
}
编译并运行该代码,输出如下:
我是重写的test方法!
七、补充
1.toString方法:
(1)toString 方法会在 println 的时候被自动调用.
(2)将对象转成字符串这样的操作我们称为 序列化
(3)toString 是 Object 类提供的方法, 我们自己创建的 Person 类默认继承自 Object 类, 可以重写 toString 方法实现我们自己版本的转换字符串方法
(4)IDEA快速生成Object的toString方法快捷键:alt+f12(insert)
不用快捷键也行,右键找到generate,然后找到toString(),最后点ok即可
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
public void show() {
System.out.println("name:"+name+" " + "age:"+age);
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("caocao",19);
person.show();
//我们发现这里打印的是一个地址的哈希值 原因:调用的是Object的toString方法
System.out.println(person);
}
}
编译并运行该代码,输出如下:
name : caocao age : 19Person@1c168e5

重写toString方法:
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
public void show() {
System.out.println("name:"+name+" " + "age:"+age);
}
//重写Object的toString方法
@Override//@Override 在 Java 中称为 "注解", 此处的 @Override 表示下面实现的 toString 方法是重写了父类的方法
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("caocao",19);
person.show();
System.out.println(person);
}
}
编译并运行该代码,输出如下:
name : caocao age : 19Person{name='caocao', age=19}
2.匿名对象
(1)没有引用的对象称为匿名对象
(2)匿名对象只能在创建对象时使用
(3)如果一个对象只是用一次, 后面不需要用了, 可以考虑使用匿名对象
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
public void show() {
System.out.println("name:"+name+" " + "age:"+age);
}
}
public class Main {
public static void main(String[] args) {
new Person("caocao",19).show();//通过匿名对象调用方法
}
}
编译并运行该代码,输出如下:
name : caocao age : 19