文章目录
面向对象
前言
因为面向对象是一块大部分,所以我分开讲了,这篇讲类和成员,上一篇讲的方法,后面讲三大特征,最后讲抽象类和接口等。
了解一下什么是面向对象?
面向对象是一种编程的思想,编程的方式。这种编程的方式是以“类和对象”为中心。
与它同等级别的其他编程思想还有:面向过程、面向函数、面向切面、面向服务编程。
经常被放在一起对比的是面向过程与面向对象。
面向过程:以“过程”为中心,以步骤或函数为最小基本单位。
面向对象:以“类和对象”为中心,以“类”为最小基本单位。
例如:把大象装进冰箱问题的解决?
面向过程:
- 第一步:把冰箱门打开
- 第二步:把大象装进去
- 第三步:把冰箱门关上
面向对象:
- 这里有几个对象参与?
- 冰箱、大象、人
- 冰箱有什么特征,有什么功能,需要完成什么事
class 冰箱{
String brand;
double length;
double width;
double height;
打开(){
}
关闭(){
}
储存(){
}
}
class 大象{
String type;
double weight;
走(){
}
}
class 人{
push(){
if(东西 是 冰箱){
冰箱.关闭();
}else if(东西是大象){
大象.走();
}
}
pull(东西){
if(东西 是 冰箱){
冰箱.打开();
}else if(东西是大象){
大象.走();
}
}
}
怎么把它们整合起来
public class 主类{
public static void main(String[] args){
人 r = new 人("张三");
冰箱 b = new 冰箱("美的",5,5,10);
大象 d = new 大象("亚洲",2);
r.pull(冰箱);
r.pull(大象);
r.push(大象);
r.push(冰箱);
}
}
问你什么是面向对象的思想?
- 找到合适的类及其对象,来解决我们的当前问题。如果核心类库中,或者第三方的类库中,有现成的类,就直接拿来用。如果没有这个合适的类,就新定义一个类,然后再用它。
什么是类和对象?
类:一类具有相同特性
的事物的抽象描述
。现实生活中,我们也会对事物进行分类管理。
相同特征:
- 基本信息,就是属性,数据,例如:姓名、年龄、电话、邮箱、性别等
- 能力或功能,就是方法,例如:学习能力,教学能力
如何声明一个类?
- 类名的命名规范:大驼峰命名法,每一个单词首字母大写,尽量见名知意。
【修饰符】 class 类名{
}
对象:对象是这一类事物的具体的个体或实体
。
public class Student{//一个类
}
Student s1 = new Student("张三",23,'男');//具体的一个对象
先有类还是先有对象呢?
- 从系统的设计和分析的角度来说,先有对象再有类,先观察需求中各个对象的共同特征,抽取出类
- 从实际编码的角度来说,必须先写类,才能new对象
类与对象的关系是什么?
-
类是创建对象的模板,设计图。比喻:造汽车的设计图。
-
对象是类的一个具体的实例(instance),实体,个体。比喻:具体的一辆能开的车。
如何new对象?
new 类名() //匿名对象
类名 对象名 = new 类名(); //有名字的对象
//对象名就是变量名而已
//变量的概念范围更大,不是所有变量都是引用对象的
//int a = 1; a也是变量,但它不是对象
//Student s = new Student(); s是变量,它引用一个对象,它才是对象名
什么是包?
在操作系统层面,或资源管理器的层面,包就是文件夹。
在Java语法的层面,包就是类的限定名。
为什么要用包?
- 方便管理众多的类
- 避免类的重名,有了包之后,类的全名称就是 包.类名
- 例如:String的全名称是 java.lang.String
- 例如:Scanner的全名称 java.util.Scanner
- 结合权限修饰符来限定某些类或类的成员的可见性范围
如何定义/声明包?
从代码的角度来说:
注意:代码必须与资源管理器的目录结构对应,对不上就会报错。
package 包名;
IDEA中如何创建包?
包的命名规范
如何跨包使用类?
- 这个类必须public才能跨包使用
- 必须加import语句或使用类的全名称
- 同一个类名的,只能有一个import语句。如果两个类同名,属于不同的包,只能一个import,一个使用全名称。或者两个都用全名称。
类的成员之一:成员变量
什么是成员变量?
定义在类中,但是在方法外的变量称为成员变量。
public class 类名{
【修饰符】 数据类型 变量名; //成员变量
【修饰符】 返回值类型 方法名(【形参列表】){ //形参也是局部变量
数据类型 变量名; //局部变量
}
}
成员变量的分类
静态变量:
- 有static修饰的成员变量,就是
静态变量
。又称为类变量。
实例变量:
- 没有static修饰的成员变量,就是
实例变量
。有的时候称为属性(笼统的说,所有的实例变量都可以叫做属性,但是更专业的说,有get/set方法才叫属性)。
package com.gj.pkg.field;
public class Employee {
public static String company; //公司 因为有static,所以它是静态变量
public String name; //姓名 因为没有static,所以它是实例变量
public int age;//年龄 因为没有static,所以它是实例变量
}
如何访问成员变量
- 跨类使用
静态变量 | 实例变量 | |
---|---|---|
跨类使用 | 类名.静态变量(标准用法) | 对象名.实例变量 |
package com.gj.pkg.field;
public class TestEmployee {
public static void main(String[] args) {
System.out.println("===========默认值============");
System.out.println(Employee.company);//null
//实例就是对象,只有通过对象才能访问实例变量
Employee e1 = new Employee();
System.out.println(e1.name);//null
System.out.println(e1.age);//0
System.out.println("===========赋值============");
Employee.company = "尚硅谷";
e1.name ="张三";
e1.age = 23;
System.out.println(Employee.company);//尚硅谷
System.out.println(e1.name);//张三
System.out.println(e1.age);//23
Employee e2 = new Employee();
System.out.println(e2.name);//null
System.out.println(e2.age);//0
System.out.println(e1.company);//非标准用法,有警告
System.out.println(e2.company);//非标准用法,有警告
}
}
- 本类使用
静态变量 | 实例变量 | |
---|---|---|
本类使用 | 既可以在本类的静态方法中用,又可以在本类的实例方法中用,没有限制 | 只能 在本类的非静态方法 中使用 |
成员变量的特点
所有成员变量都有默认值
- byte,short,int,long:0
- float,double:0.0
- char:\u0000的空字符
- boolean:false
- 引用数据类型(包括String,数组等):null
值的独立性和共享性
静态变量 | 实例变量 | |
---|---|---|
独立性和共享性 | 所有对象共享的 | 每一个对象都是独立的 |
成员变量的内存分析
变量的本质就是代表一块内存区域,用于存储数据的。内存应该有内存地址,但是不便于记忆,需要给内存取名字,变量名就是给内存取名字。
变量有三要素:变量名、数据类型、变量值。数据类型决定了变量所占用的内存大小。
一个对象在堆中的内存分为3个部分:
- 对象头:用于存储对象所属类的指针、对象的hashcode值、锁标记、GC标记等(今天不讨论)
- 实例数据区:用于存储对象的实例变量的值
- 填充空白(不是所有对象都有的):当某些类有boolean类型的变量时,1个boolean的变量占二进制的1位,但是内存管理是以“字节“为单位,当我们有1个boolean值后,1个字节就会有7位是空的,那么为了内存的”对齐“管理方便,这7位就是填充空白。
成员变量是引用数据类型
案例1
package com.gj.pkg.field;
public class Husband {//丈夫类
public String name;
public Wife wife;
//Wife在这里是数据类型,是类(class)类型
}
package com.gj.pkg.field;
public class Wife {//妻子类
public String name;
public Husband husband;
//Husband也是数据类型,是类(class)类型
}
package com.gj.pkg.field;
public class TestHusbandWife {
public static void main(String[] args) {
Husband h = new Husband();
h.name = "光头强";
Wife w = new Wife();
w.name = "翠花";
h.wife = w;//翠花嫁给光头强
w.husband = h;//光头强娶翠花
System.out.println("丈夫的姓名:" + h.name +",他的妻子的姓名:" + h.wife.name);
System.out.println("妻子的姓名:" + w.name +",她的丈夫的姓名:" + w.husband.name);
}
}
案例2
在com.gj.exer5
包中
(1)声明一个MyDate
类型,有属性:年year(int类型),月month(int类型),日day(int类型)
(2)在测试类TestMyDate
类中,创建两个日期对象,一个日期赋值为今年六一儿童节日期,一个日期赋值为今年程序员节日期,并显示
(3)声明另一个Employee
类型,有属性:姓名name(String类型),生日birthday(MyDate类型)
(4)在测试类TestEmployee
中的main
中,创建两个员工对象,并为他们的姓名和生日赋值,并显示
package com.gj.exer5;
/*
(1)声明一个MyDate类型,有属性:年year(int类型),月month(int类型),日day(int类型)
*/
public class MyDate {//日期类
public int year;
public int month;
public int day;
//它们是成员变量,没有static修饰,所以也叫实例变量
}
package com.gj.exer5;
public class TestMyDate {//测试类
public static void main(String[] args) {
//创建两个日期对象,一个日期赋值为今年六一儿童节日期,
// 一个日期赋值为今年程序员节日期,并显示
MyDate m1 = new MyDate();
m1.year = 2024;
m1.month = 6;
m1.day = 1;
MyDate m2 = new MyDate();
m2.year = 2024;
m2.month = 10;
m2.day = 24;
//输出信息
System.out.println("今年的六一儿童节:" + m1.year + "年" + m1.month + "月" + m1.day + "日");
System.out.println("今年的程序员节:" + m2.year + "年" + m2.month + "月" + m2.day + "日");
}
}
package com.gj.exer5;
/*
(3)声明另一个Employee类型,有属性:姓名name(String类型),生日birthday(MyDate类型)
*/
public class Employee {
public String name;
public MyDate birthday;
}
package com.gj.exer5;
public class TestEmployee {
public static void main(String[] args) {
//创建两个员工对象,并为他们的姓名和生日赋值,并显示
Employee e1 = new Employee();
e1.name = "熊大";
// e1.birthday = new MyDate();//只要是引用数据类型的变量,就可以给它赋值对应类型的对象
/*
(1)编译时(javac命令),由编译器来检查语法格式等
这里编译没有报错,
①是因为e1这个变量是Employee类型的,然后e1可以.出name和birthday,因为Employee类定义了这两个成员变量
.有个专业的名称,称为成员操作符,用来访问“成员”
②e1.birthday也是变量,是MyDate类型的,它能.出year、month、day,
因为MyDate类定义了这3个成员变量
③语法正确,再看数据类型
2000,1,9都是整数,可以给year,month,day,因为它们也是int类型
(2)运行时(java命令)
运行时就要检查e1,birthday这些.前面的变量是否有对象
*/
e1.birthday.year = 2000;
e1.birthday.month = 1;
e1.birthday.day = 9;
Employee e2 = new Employee();
e2.name = "熊二";
MyDate m2 = new MyDate();
m2.year = 2001;
m2.month = 5;
m2.day = 8;
// e2.birthday = m2;
//输出员工的信息
System.out.println("员工的姓名:" + e1.name +",生日:" + e1.birthday.year+"年" + e1.birthday.month+"月" + e1.birthday.day+"日");
System.out.println("员工的姓名:" + e2.name +",生日:" + e2.birthday.year+"年" + e2.birthday.month+"月" + e2.birthday.day+"日");
}
}
类的成员之二:成员方法
成员方法的分类
静态方法
- 如果有static修饰的方法,就是静态方法,又称为类方法。
实例方法
- 没有static修饰的方法,就是实例方法,通常也称为非静态方法。
思考:什么时候该用静态方法,什么时候该用实例方法呢?
原则:如果方法中只访问静态变量(或者扩大的话,只访问本类的静态成员),那么这个方法可以是
是静态方法,也可以是实例方法
。
但是如果方法中涉及到访问实例变量(或者扩大的话,设计到访问本类的其他非静态成员),那么这个方法必须
是实例方法
。
package com.gj.pkg.method;
public class Account {//银行账号
public static double rate;//年利率
public String id;//卡号
public double balance;//余额
//上面的3个成员变量中,rate是静态变量,id和balance是实例变量
//实例变量依赖于对象
//定义方法,返回利息,利息的简单算法是 利息 = 余额 * 利率
//因为这个方法中涉及到实例变量balance,这个方法只能是实例方法,不能是静态方法
public double getInterest(){
return balance * rate;
}
//定义方法,返回月利率,月利率的简单算法 = 年利率 ÷ 12
//因为这个方法中不涉及实例变量等其他非静态成员,那么这个方法可以是静态的,也可以是非静态的
//一般如果这个方法是工具类性质的方法,一般选静态的,否则选非静态的
//例如:数学工具类Math类,数组工具类Arrays,集合工具类Collections等,它们中全部是静态方法
//这个并没有语法上严格的界限,定义为静态方法更方便
public static double getMonthlyRate(){
return rate / 12;
}
}
package com.gj.pkg.method;
public class TestAccount {
public static void main(String[] args) {
Account.rate = 0.35;
System.out.println("月利率:" + Account.getMonthlyRate());
//System.out.println("利息:" + Account.getInterest());//报错,没有对象
//实例方法,实例变量等依赖于对象
Account a = new Account();
a.id = "1111111";
a.balance = 100000000000L;
System.out.println("a账号的余额:" + a.balance +",利息:" + a.getInterest());
//a账号的余额:1.0E11,利息:3.5E10科学计数法
}
}
如何调用方法
- 跨类调用
静态方法 | 实例方法/非静态方法 | |
---|---|---|
跨类调用 | 类名.方法(【实参】) | 对象名.方法(【实参】) |
- 本类调用
静态方法 | 实例方法/非静态方法 | |
---|---|---|
本类调用 | 随便用 | 只能在其他非静态方法中使用 |
同一个类中成员互相使用的总结
再次体会为什么要用方法?
方法是一个独立的可复用的功能。换句话,当一段代码需要反复被使用,就可以封装为一个方法。
案例1
写法1:没有定义方法
package com.gj.pkg.method;
public class Rectangle {//矩形,长方形
public double length;//长
public double width;
}
package com.gj.pkg.method;
public class TestRectangle1 {
public static void main(String[] args) {
Rectangle r1 = new Rectangle();
r1.length = 8;
r1.width = 6;
Rectangle r2 = new Rectangle();
r2.length = 10;
r2.width = 5;
double area1 = r1.length * r1.width;
double area2 = r2.length * r2.width;
double perimeter1 = (r1.length + r1.width) * 2;
double perimeter2 = (r2.length + r2.width) * 2;
System.out.println("r1的长:" + r1.length +",宽:" + r1.width +",面积:" + area1 +",周长:" + perimeter1);
System.out.println("r2的长:" + r2.length +",宽:" + r2.width +",面积:" + area2 +",周长:" + perimeter2);
}
}
写法2:声明方法
package com.gj.pkg.method;
public class Rectangle {//矩形,长方形
public double length;//长
public double width;
public double area(){
return length * width;
}
public double perimeter(){
return 2 * (length + width);
}
public String getInfo(){
return "长:" + length +",宽:" + width +",面积:" + area() +",周长:" + perimeter();
}
}
package com.gj.pkg.method;
public class TestRectangle1 {
public static void main(String[] args) {
Rectangle r1 = new Rectangle();
r1.length = 8;
r1.width = 6;
Rectangle r2 = new Rectangle();
r2.length = 10;
r2.width = 5;
System.out.println("r1的" + r1.getInfo());
System.out.println("r2的" + r2.getInfo());
}
}
案例2
package com.gj.pkg.method;
/*
(1)声明一个MyDate类型,有属性:年year(int类型),月month(int类型),日day(int类型)
*/
public class MyDate {//日期类
public int year;
public int month;
public int day;
//它们是成员变量,没有static修饰,所以也叫实例变量
public void setDate(int y, int m, int d){
year = y;
month = m;
day = d;
}
public String getDateInfo(){
return year + "年" + month + "月" + day + "日";
}
}
package com.gj.pkg.method;
public class TestMyDate {//测试类
public static void main(String[] args) {
//创建两个日期对象,一个日期赋值为今年六一儿童节日期,
// 一个日期赋值为今年程序员节日期,并显示
MyDate m1 = new MyDate();
m1.setDate(2024,6,1);
MyDate m2 = new MyDate();
m2.setDate(2024,10,24);
//输出信息
System.out.println("今年的六一儿童节:" + m1.getDateInfo());
System.out.println("今年的程序员节:" + m2.getDateInfo());
}
}
package com.gj.pkg.method;
/*
(3)声明另一个Employee类型,有属性:姓名name(String类型),生日birthday(MyDate类型)
*/
public class Employee {
public String name;
public MyDate birthday;
public void setEmployeeInfo(String n, int y, int m, int d){
name = n;
birthday = new MyDate();
birthday.year = y;
birthday.month = m;
birthday.day = d;
}
public String getEmployeeInfo(){
// return "姓名:" + name +",生日:" + birthday.year+"年" + birthday.month+"月" + birthday.day+"日";//对
return "姓名:" + name +",生日:" + birthday.getDateInfo();
}
}
package com.gj.pkg.method;
public class TestEmployee {
public static void main(String[] args) {
//创建两个员工对象,并为他们的姓名和生日赋值,并显示
Employee e1 = new Employee();
e1.setEmployeeInfo("熊大",2000,1,9);
Employee e2 = new Employee();
e2.setEmployeeInfo("熊二",2001,5,8);
//输出员工的信息
System.out.println("员工1的" + e1.getEmployeeInfo());
System.out.println("员工2的" + e2.getEmployeeInfo());
}
}
成员小结
静态变量与实例变量
静态变量 | 实例变量 | |
---|---|---|
有没有static | 有 | 无 |
内存位置 | 方法区 | 堆 |
共享还是独立 | 共享 | 独立 |
初始化时机 | 类加载后就初始化 | new对象时初始化 |
本类中谁能用它 | 静态方法 + 实例方法 | 实例方法 |
其他类如何用它 | 类名.静态变量 | 对象名.实例变量 |
静态方法和实例方法
静态方法 | 实例方法/非静态方法 | |
---|---|---|
有没有static | 有 | 无 |
本类中谁能用它 | 静态方法 + 实例方法 | 实例方法 |
其他类如何用它 | 类名.静态方法 | 对象名.实例方法 |
在本类中它能用谁 | 只能用本类的静态变量,静态变量 | 可以使用本类的所有成员:静态变量、静态方法、实例变量、实例方法 |
面向对象三大基本特征oop
封装:将属性和方法打包成一个对象,同时利用public protect 缺省 private修饰符来隐藏对象的内部实现细节,只暴露必要的公共接口供外部访问。这样做的好处可以保护对象不被外界随意修改,增加安全性。
继承:允许子类可以继承父类的成员变量和成员方法。复用父类的代码,同时可以扩展自己特有的功能。减少了代码重复,使得程序结构更加清晰和模块化。
多态:一个方法的多种形态,多态有两种形式,编译时多态:方法重载,运行时多态:方法重写。运行时多态的具体表现形式是父类或接口的引用指向子类对象,编译时候看左边父类或接口类型来确定我要调用哪个方法,运行时看等号右边实际对象类型决定调用哪个方法实现。这样编写代码更灵活,扩展性强。