目录
一、面向对象程序设计的三条主线
一、设计类
二、新建类的对象。
三、通过对象来调用类的属性和方法
二、面向对象的三大特点
一、封装
二、继承
三、多态
三、如何理解类和对象
类:类是抽象的、概念性的。
对象:对象是类的派生,是 new 出来的,是实实在在存在的,在内存中占据着一定的空间。
在 Java 中,万事万物皆对象
一、在 Java 语言本身的范畴中,我们都将功能、属性等封装到类中,通过类的实例化来调用具体的功能和属性。
二、在 Java 与前端 html、后端数据库进行交互时,前后端的结构在与 Java 层面对应是,都体现为类。
四、创建一个类的内存解析
new 操作符的本意是分配内存。程序执行到 new 操作符时,因为知道了类型,才能知道分配多大的内存空间。分配完内存后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化;构造方法返回后,一个对象创建完毕,可以把他的引用发布到外部,在外部就可以操纵这个对象了。
如图:
(新建一个对象的内存解析)
初始化的对象属性都是默认值,字符串类型默认为 null,整形默认为 0。
当使用对象访问时,实际上时通过操作地址,访问该地址指向的内存,来访问变量。
代码实例:
public class TestStudent {
public static void main(String[] args) {
//新建一个 student 对象,并未对其进行赋值
Student stdu1 = new Student();
System.out.println("stdu1: " + stdu1 + ", stdu1.name: " + stdu1.name + ", stdu1.age: " + stdu1.age);
//输出stdu1: ObjectTest.Student@1540e19d, stdu1.name: null, stdu1.age: 0
}
}
//定义 Student 类
class Student {
String name;
int age;
public Student() {
}
}
对象是引用数据类型,所以当把对象赋值给一个变量时,其实是赋的它的地址
例:
public class TestStudent {
public static void main(String[] args) {
Student stdu1 = new Student();
stdu1.name = "张三";
stdu1.age = 10;
System.out.println("stdu1: " + stdu1 + ", stdu1.name: " + stdu1.name + ", stdu1.age: " + stdu1.age);
//新建一个对象,指向 stdu1
Student stdu2 = stdu1;
System.out.println("stdu2: " + stdu2 + ", stdu2.name: " + stdu2.name + ", stdu2.age: " + stdu2.age);
//通过 stdu2 来更改 stdu1 的属性值
stdu2.name = "李四";
System.out.println("stdu1: " + stdu1 + ", stdu1.name: " + stdu1.name + ", stdu1.age: " + stdu1.age);
}
}
/*结果:
stdu1: ObjectTest.Student@1540e19d, stdu1.name: 张三, stdu1.age: 10
stdu2: ObjectTest.Student@1540e19d, stdu2.name: 张三, stdu2.age: 10
stdu1: ObjectTest.Student@1540e19d, stdu1.name: 李四, stdu1.age: 10
*/
可以看到,stdu1 和 stdu2 指向同一个内存地址,并且通过 stdu2 改变了 stdu1;
五、对象数组的内存解析
创建一个对象数组,其实是在对空间中分配了对应大小的空间,然后返回改数组的首地址,如同上述所说,对象只能存储引用,所以数组中存储的实际上是对象的地址,如果不对其进行初始化,则为 null,如图:
例:
public static void main(String[] args) {
Student[] stdus = new Student[8];
//输出数组地址
System.out.println("stdus: " + stdus);
//遍历未初始化的数组
for(int i = 0; i < 8; i++) {
System.out.print(stdus[i] + " ");
}
//初始化 stdus[0] 并且输出
stdus[0] = new Student();
System.out.println("\nstdus[0] " + stdus[0] + ", stdus[0].name: " + stdus[0].name + ", stdus[0].age: " + stdus[0].age);
}
/*运行结果:
stdus: [LObjectTest.Student;@1540e19d
null null null null null null null null
stdus[0] ObjectTest.Student@677327b6, stdus[0].name: null, stdus[0].age: 0
*/
六、属性(成员变量)和局部变量
1、相同点:
1.1 定义的格式相同:都是 变量类型 变量名 = 值
1.2 都有其对应的作用域
2、不同点:
2.1 定义的位置不同
2.2 存储的位置也不同
2.3 修饰符不同,局部变量不能用 public 等关键字来修饰
七、方法的重载
一个类中两个同名的方法,参数列表(参数个数或者参数类型)不同就构成了重载。
如何确定使用哪个方法:在调用方法的过程中,先看方法名,如果方法名相同,再看参数列表。
注:重载与返回值类型无关,因为如果两个方法如果参数列表相同,仅返回值类型不同,则无法确定使用哪个方法。
方法重载的典型示例:
public class OverLoadTest {
public static void main(String[] args) {
int[] array1 = new int[]{1, 2, 3}; // int 类型的数组
char[] array2 = new char[]{'a', 'b', 'c'}; // char 类型的数组
System.out.println(array1);
System.out.println(array2);
}
}
/*结果:
[I@1540e19d
abc
*/
分析原因:使用 IDE 打开调用的方法,发现调用的是两个不同的方法:
输出 int 类型的数组时,调用的是 PrintStream 类中 public void println(Object x) 方法;而输出 char 类型的数组时,调用的是 PrintStream 类中 public void println(char x[]) 方法。
八、可变个数的形参
可变个数的形参语法是: 参数类型 … 参数名,实现的机制就是在参数中使用数组实现的,访问每个参数的方式也与访问数组的方式相同。
例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gzvKydRR-1583279888269)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\1582553018597.png)]
如图所示的两个 show 函数发生了编译错误,错误原因就是函数重复定义了,可以证明 … 和数组的实现方式相同。
例:
public class ParameterTest {
public static void main(String[] args) {
ParameterTest pt = new ParameterTest();
pt.show("成龙", "李连杰", "洪金宝");
}
//访问可变参数中的参数
private void show(String ... names) {
for(int i = 0; i < names.length; i++) // names 也有 length 方法
System.out.println(names[i]); // 访问方式也是通过下标访问
}
}
注:可变参数必须声明在最后一个参数的位置,因为如果不这样做就无法确定到底有几个参数了;进而,一个函数中,最多有一个可变参数。
九、对构造器的理解
构造器不要理解成构造函数,因为在 Java 中,函数都是通过对象来访问的,而构造器是生成对象的。在构造器中,可以调用其他构造器,但如过有 n 个构造器,最多有 (n - 1) 个可以调用其他构造器,另一个是调用的 super() 。
例:
public class Person {
int age;
String name;
public Person() {
System.out.println("我是父类");
}
}
class Student extends Person {
int id;
public Student() {
System.out.println("我是构造器1");
}
public Student(int id) {
this();
this.id = id;
System.out.println("我是构造器2");
}
public Student(int age, String name) {
this(3);
this.name = name;
this.age = age;
System.out.println("我是构造器3");
}
}
/*运行结果
我是父类
我是构造器1
我是构造器2
我是构造器3
*/
分析结果:可以看到,Student() 构造器并没有显示调用 super(),但还是输出了 “我是父类”,说明子类会自动调用父类的构造器。
十、属性赋值
1、默认值
2、显示赋值
3、构造器中赋值
4、通过对象进行赋值
十一、this 关键字
定义:this 关键字表示调用该方法对象的引用。
public class ThisTest {
public static void main(String[] args) {
ThisTest t = new ThisTest();
System.out.println(t);
t.getInfo();
}
public void getInfo() {
System.out.println(this);
}
}
/*结果
ObjectTest.ThisTest@1540e19d
ObjectTest.ThisTest@1540e19d
*/
分析结果:上述结果表明,this 和 t 是同一个地址,所以印证了 this 的定义。