深入理解java中的面向对象
类和对象
面向过程:当我们需要实现一个功能时,我们专注于实现过程的每一个步骤即实现的过程。
面向对象:实现功能时,我们不关注具体的实现步骤,而是找一个已经具有此功能的人,去帮我们实现这个功能。
那么什么是对象?
Object:对象,东西,万物皆为对象。
同类型的东西,我们把它抽象成一个类。类是对象的模板、图纸,是对象的数据结构定义。
通常一个类是不能直接被使用的,需要根据该类创建一个对象才能使用,那么我们首先要定义一个类
定义类
- 其实定义类,就是在描述事物,就是在定义属性(变量)和方法(函数)。
- 类中可以声明:属性,方法,构造器;
- 属性(变量)分为:实例变量,局部变量;
- 实例变量:用于声明对象的结构的,在创建对象时候分配内存,每个对象有一份!实例变量(对 象属性)在堆中分配,并作用于整个类中,实例变量有默认值,不初始化也能参与运算;
- 局部变量在栈中分配,作用于方法或语句中,必须初始化,有值才能运算。
我们来构造一个学生类:
分析他们的属性和行为
构建学生类
1.属性(特性)
性别:
学号:
专业:
2.动作(行为)
上课()
吃饭()
睡觉()
那么对应到java类中:
成员变量:(属性) int sex;性别 int number;学号 String major;专业 成员方法:(动作或者行为) public void learning(){} 上课 public void eat(){} 吃饭 public void sleep(){} 睡觉
1.成员变量(属性)是直接定义到类里面的,方法外面
2.成员方法(动作和行为)不要static修饰
定义在方法中的变量称为局部变量。
成员变量和局部变量的区别:
- 内存位置不一样(了解)
成员变量存在内存的堆内存中
局部变量存放在内存的栈内存中 - 定义的位置不同:(重点)
成员变量:类中方法之外
局部变量:方法内部 - 作用范围不一样:(重点)
成员变量:作用范围存在整个类中
局部变量:只能作用在对应方法中,出了方法就没用了 - 默认值不一样(重点)
成员变量:如果没有赋值,则会自动补上默认值
局部变量:没有默认值,需要自己手动补全(参数也是一种局部变量,他的赋值是由调用者提供的) - 生命周期不一样(了解)
成员变量:根据对象创建时生成,被GC回收时消失
局部变量:根据方法入栈时创建,出栈时就消失了
Java面向对象的三大特征
封装,继承,多态:
1.封装---属性私有化,行为公开化
权限关键字 public procted default private
2.继承---父子类(将一些重复的代码抽取到父类当中,单一继承)
3.多态---向上向下造型
封装(访问控制):
封装:将数据封装到类的内部,将算法封装到方法中。
- 封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其 访问,通常有两种访问方式:set 设置,get 获取。
- 封装结果:存在但是不可见。
- public:任何位置可见,可以修饰:类、成员属性、成员方法、内部类、跨包访问类 (需要使用import 语句导入),成员属性 == 成员变量。
- protected:当前包中可见,子类中可见。可以修饰:成员属性、成员方法、内部类(只 能在类体中使用,不能修饰类) 。
- default(默认的):当前包内部可见,就是没有任何修饰词,可以修饰:类、成员属性、成员方 法、内部类,但在实际项目中很少使用。默认类(包内类)的访问范围:当前包内部可见, 不能在其他包中访问类,访问受限!main 方法若定在默认类中 JVM 将找不到,无法执行, 因此必定在public 类中。
- private:仅仅在类内部可见。可以修饰:成员属性、成员方法、内部类(只能在类体 中使用,不能修饰类) 。私有的方法不能继承,也不能重写。
构建标准JavaBean
使用private关键字修饰需要被保护的成员变量,外部无法随意访问
只有本类可以正常使用该变量
外部想访问需要通过Getter/Setter方法
getXX和setXX 命名规则
- 对于 set方法不能有返回值,参数值必须与成员变量类型保持一致
- 对于 get方法不能有参数值,返回值必须返回成员变量且返回值类型需要与成员变量保持一致
构造器
- 用于创建对象并初始化对象属性的方法,叫“构造方法”,也叫“构造器”;构造器在类 中定义。
- 构造方法的方法名和类名一样,无返回值(void也没有)public修饰
N个属性:2^N+1个构造方法 - 方法的重载:方法名相同,参数列表不同
1.重载方法不能有返回值
2.如果没有写构造方法 那么系统会赠送一个无参构造方法,如果我们写了任何构造方法 系统将不再赠送 - new对象其实就是使用该类的无参构造
标准javaBean规范
属性私有化
行为公开化
//构建标准JavaBean public class Person { private String name; private String age; //无参构造方法 public Person(){ } //全参构造方法 public Person(String name, String age) { this.name = name; this.age = age; } //get方法 public String getName() { return name; } //set方法 public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
继承
父子类:将重复的代码提取到父类当中,单一继承
- java中不支持多继承,一个子类只能继承一个父类,一个父类可以存在多个子类
- 父子类中:
- 父类无法访问子类的属性和方法
- 子类可以访问全部父类的属性和方法(当子类没有的属性时,会自动向上查找)
- 出现同名的变量遵循就近原则
super.method(); //使用父类的方法super. System.out.println(this.num); //调用自己的成员变量使用this关键字. System.out.println(super.num); //想使用父类的变量super.
方法的重写(override):方法名相同,参数列表也相同
重写发生在父子类中
遵循“两同两小一大”
- 两同:方法名相同,参数列表相同
- 两小:子类方法的返回值类型要小于或等于父类
- 父类为void子类必须要相等
- 父类为基本类型时,必须要相等
- 父类为引用类型时,子类小于或者等于父类
子类抛出的异常要小于等于父类
- 一大:
- 子类访问权限大于等于父类 --public >protected>private
方法的重载(overload):方法名相同,参数列表不同
多态
向上造型:父类引用指向子类对象(子类型,实例也是子类的实例化);
当发生向上造型时,调用同名方法程序会先到父类中寻找该方法,再到子类中寻找
Person p = new Studnet();
代码中的多态,其实就是父类引用指向子类对象(向上造型)
格式
父类名称 对象名 = new 子类名称();或者
接口名 对象名 = new 实现类名称();
使用多态访问成员变量的形式:
- 对于直接同过对象访问变量:要看等号左边部分,左边部分是谁就优先用谁,没有则往上找
- 间接访问成员变量:该方法属于谁,优先用谁,没有则往上找
访问成员方法:
- 看new 的是谁则优先用谁,没有则往上找
口诀:
- 成员方法:编译看左边,运行看右边
- 成员变量:编译看左边,运行还是看左边
为什么要使用向上造型:
- 向上造型为我提供了统一的数据类型
向下造型:子类引用指向父类对象(父类型,实例是子类的实例化);
向下造型格式:
- 父类名称 父类对象名 = NEW 子类名称(); double a = 10
- 子类名称 子类对象名 = (子类对象名)父类对象 (相当于一个数据的还原) int b = (int)10.0
instanceof关键字 用来判断一个父类引用的对象,他本来的子类是什么
用来判断一个对象能不能被当做后面类型的实例 得到一个boolean值
格式 对象 instanceof 类名称
Person p1 = new Student(); Student s1 = (Student)p1; 但有运行出错的情况: Person p2 = new Person(); Son s2 = (Son)f2;//编译无错但运行会出现错误 在不确定父类引用是否指向子类对象时,可以用instanceof来判断: if(p3 instanceof Student){ Student s3 = (Student)p3; }
常用关键字
static静态修饰词:
可以修饰变量,可以修饰方法
static修饰的变量或者方法在程序加载时候就被加载到内存中,且只被加载一次
使用static修饰的变量不再属于对象,而是全局共享的数据 静态变量的调用: 1>对象.变量 ----不推荐 2>类名.变量 ----推荐 静态方法不能使用非静态资源
静态块:格式static{ }
静态块加载时间为类加载时加载时 执行
静态块只在类加载时候 加载一次
何时使用:加载/初始化静态资源(视频,音频,图片等)
void 标注无返回值
有返回值方法格式:修饰符 返回值类型 方法名(参数列表){代码块 return结果};
形参 和 实参
形参:方法中标注的参数即为形参
实参;调用方法时实际传过去的参数
public class Method_Demo2 { public static void main(String[] args) { f1(); f2(5); double a = f3(); f4("Hello"); } // 无参无返回值方法 private static void f1() { System.out.println("Hello World"); } // 有参有返回值方法 private static int f2(int x) { int a = 5+x; return a; } // 无参有返回值方法 private static double f3() { double a = 2.5; return a; } //有参无返回值方法 private static void f4(String str) { System.out.println(str); } }
package:
- 避免了类名的冲突
- 同包下的类名不能相同,不同包下面的类名可以相同
- 对于一个类名来说他的全称:包名+类名
- 对于包名最好全部都是小写字母
import:
- 同包中的类可以直接访问,不同包中的类不能直接访问
- 不同包中类访问方式:
- 使用import关键字 先声明再使用
- 使用类的全称 --不建议
public protected private 访问修饰符
public 表示共用的,任何类都可以访问
protected 表示受保护的 访问范围可以被 本类 同包类 子类 访问
default什么都不写:表示默认级别 访问范围 本类 同包类
private:表示私有 访问范围本类
1)对于类来说修饰词只有public或默认两种级别
2)类里面的成员可以以上四种修饰词修饰访问权限能够用下一级别的修饰词尽量使用下一级别(如可以用public用protected)
final:最终的
- 修饰变量需要马上赋值,且不能被改变
- 修饰方法:方法不能被重写 可以被重载
- 修饰类:类不能被继承
static final修饰的变量称为常量
- 声明的同时必须要赋值
- 访问 通过类名.常量名
- 常量名的声明:建议全部使用大写字母 多个字母使用下划线连接 WIDTH_HEIGHT
- 在编译时 常量直接替换为数字
如果一个数据不被改变且经常会用到可以把他做成一个常量
abstrat
抽象方法:使用关键字abstract修饰 没有方法体
抽象类:抽象方法所在的类,必须为抽象类,格式 直接在class前面加abstract修饰词
抽象方法和抽象类的特点:
- 抽象类不能被实例化(不能被new出来)
- 子类必须要重写父类里面的抽象方法
- 重写抽象方法时需要去掉abstract关键字
- 抽象类中可以没有抽象方法
- 但是有抽象方法的类一定是抽象类
- abstract只能用来修饰方法或者类
抽象类存在的意义:
- 1.分装子类共有的行为
- 2.为所有子类提供了统一的类型 ---向上造型使用的
- 3.可以包含抽象方法,为所有子类提供了统一的入口,子类具体的实现不同,但方法定义相同
成员内部类(实际使用率很低)
- 类中套类,外面的类称为Outer,里面的类称为Inner
- 内部类通常只服务于外部类,对外没有可见性
- 内部类通常是在外部类中创建的
- 内部类可以访问外部类私有属性,因为内部类中有个隐式的引用指向创建他的外部类对象:外部类名.this
内部类的主要作用如下:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
- 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
- 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
抽象类和接口
抽象类可以继承普通类
抽象类可以实现接口,且不用重写接口中的方法
抽象类可以被承
接口 可以解决类的单一继承 接口的定义: 接口是多个类的公共规范 接口也是一种数据类型,其中最重要的就是里面的抽象方法 接口的格式 /** * public interface 接口名{ * //接口内容 * } * 接口编译后也是.class * * 接口中内容: * java 1.7版本接口内容 * 1.常量 * 2.抽象方法 * java 1.8 * 3.默认方法 * 4.静态方法 * java 1.9 * 5 私有方法 从Java 1.8开始可以添加默认方法 * 接口中默认方法的格式: * public default 返回值类型 方法名(参数){ * 方法体 * }; * 默认方法的添加可以解决接口升级的问题 * * 从Java1.8开始可以添加静态方法: * 格式: * public static 返回值 方法名(参数列表){ * 方法体 * } * java 1.9开始允许定义私有方法: * 格式: * 静态私有方法 * private static 返回值类型 方法名(){ * 方法体 * } * 默认私有方法 * private 返回值类型 方法名(参数列表){ * 方法体 * } * 私有方法的调用:都是直接使用方法名调用 * 创建私有方法为了解决代码重复 * * */ 接口中的数据都是常量: pubic final static 数据类型 常量名 = 常量值 接口中的数据都为常量:不详细写清楚也会自动补齐 接口中的方法为抽象方法 想使用接口中的方法需要实例化接口的实现类,实现类需要重写接口中的方法, 如果不全部重写抽象方法,实现类需要为抽象类 1.接口中默认方法的调用:可以通过实现类的对象,直接调用 2.接口中的默认方法,可以被实现类覆盖重写重写 静态方法的调用 接口名.方法名(参数列表) 注意:不能同实现类对象.静态方法
注意:接口可以继承接口,接口不可以实现接口,接口可以多继承,一个类可以实现多个接口,接口无法被实例化