java入门12
面向过程与面向对象
-
面向过程思想
- 步骤清晰,线性思维
- 适合处理简单问题
-
面向对象思想
-
分类思维,对不同类的任务单独思考
-
适合复杂问题,多人合作
-
什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程本质:以类的方式组织代码,以对象的组织封装数据
-
抽象
-
三大特性
- 封装
- 继承
- 多态
-
从代码角度,先有类后有对象,类是对象的模板
类与对象的关系
- 类是对某一类事物的定义,是抽象的数据类型,对象是抽象概念的具体实例
创建与初始化对象
- 使用new关键字创建对象
- 使用new时,除了分配内存空间,还会为创建的对象进行默认初始化以及对类中构造器的调用
- 类中的构造器也叫构造方法,是创建对象时必须调用的,构造器特点:
- 必须和类名相同
- 必须没有返回类型,也不能写void
比如创建一个学生类:
package oop;
//学生类
public class Student {
//属性
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");
}
}
实例化对象:
package oop;
public class Demo01 {
public static void main(String[] args) {
//实例化
//类实例化后会返回一个自己的对象
//student对象就是一个Student类的具体实例
Student srrdhy1 = new Student();
Student srrdhy2 = new Student();
srrdhy1.name = "艾伦耶格尔";
srrdhy1.age = 19;
System.out.println(srrdhy1.name);
System.out.println(srrdhy1.age);
srrdhy1.study();
//艾伦耶格尔
//19
//艾伦耶格尔在学习
}
}
有参构造与无参构造
类:
package oop;
public class Hero {
//一个类即使什么也不写,也有一个方法
//显示的定义构造器
String name;
//实例化初始值
//使用new关键字,本质在调用构造器
//用来初始化值
public Hero(){
this.name = "奥克斯";
}
//有参构造:一旦定义了有参构造,无参必须显示定义
public Hero(String name){
this.name = name;
}
}
主函数执行:
package oop;
public class Demo01 {
public static void main(String[] args) {
//实例化一个对象
Hero hero1 = new Hero();
Hero hero2 = new Hero("黑魔");
System.out.println(hero1.name);
System.out.println(hero2.name);
//奥克斯
//黑魔
}
}
//alt + insert用于生成构造器
public Hero(String name) {
this.name = name;
}
public Hero() {
}
选择Constructor,直接OK生成有参构造,选择Select None生成无参构造。
封装
-
程序设计追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量方法给外部使用
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
-
属性私有,get/set
创建一个类:
package oop.demo02;
//学生类 private:私有 不能直接在main方法调用了
//这时我们使用public的get和set方法来操作
public class Student {
//名字
private String name;
//学号
private int id;
//性别
private char sex;
//get获得这个数据
public String getName(){
return this.name;
}
//set给这个数据设置值
public void setName(String name){
this.name = name;
}
主函数:
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("srrdhy");
String name1 = s1.getName();
System.out.println(name1);//srrdhy
}
}
快捷操作:
//alt + insert选择Getter and Setter
//自动生成
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
现在添加一个年龄:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
设置一个999岁,但999年龄是不合理的:
s1.setAge(999);
System.out.println(s1.getAge());//非法年龄
规避操作:
public void setAge(int age) {
if (age<120 && age>0){
this.age = age;
}else {
System.out.println("输入非法");
}
}
封装好处:
- 提高程序安全性,保护数据
- 隐藏代码实现细节
- 统一接口get、set
- 系统可维护性提高
继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- extends意为扩展,子类是父类的扩展
- java类只有单继承(一个子类不能有多个父类)
- 除了继承,类与类间的关系还有依赖、组合、聚合
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示
- 子类与父类间从意义上具有"is a"的关系
- object类
- super
- 方法重写
我们建立一个父类并赋予一个方法:
//人类 父类
public class Person {
public void speak(){
System.out.println("说了些什么");
}
}
建立一个子类继承父类,什么方法也没有写:
//学生 子类
public class Student extends Person{
}
这时在主程序中new一个Student对象,使用speak():
public class Main {
public static void main(String[] args) {
Student student1 = new Student();
student1.speak();
}
}
可以输出,这表示子类可以调用父类的方法
再比如我们在父类中增加
public int money = 10_0000_0000;
调用:
System.out.println(student1.money);
也可以输出,子类会继承父类的一切
但如果我们把父类的money改为私有的:
private int money = 10_0000_0000;
子类就调用不了了
对于调用权限,由高到低为:
public
protected
default
private
使用快捷键ctrl+H会生成树结构(要把鼠标点在需要的类里)
可以发现,父类上还有一个类,因为在java中,所有的类默认继承Object类
现在我们在父类建立一个名字,采用protected。
public class Person {
protected String name = "srrdhy";
}
子类中用private建立一个不同的名字,再写一个输出名字的方法,分别输出三种名字:
public class Student extends Person{
private String name = "琪亚娜";
public void printname(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
主程序运行:
public class Main {
public static void main(String[] args) {
Student student1 = new Student();
student1.printname("雷电芽衣");
}
//雷电芽衣
//琪亚娜
//srrdhy
}
如果我们在父类中写一个print方法:
public void print(){
System.out.println("Person");
}
又在子类中写一个同名方法:
public void print(){
System.out.println("Student");
}
那么主程序调用print方法时,会调用子类的print
注意,private无法继承,super也不行
现在我们在子类父类各写一个无参构造:
public Student() {
System.out.println("Student无参执行");
}
public Person() {
System.out.println("Person无参执行");
}
主程序中单纯new一个student,什么也不做,会输出:
Person无参执行
Student无参执行
先输出了Person无参执行,说明public Student()里隐藏调用了父类的无参构造,隐藏了一个super()
调用父类的构造器,必须要在子类的第一行
父类没有无参时,子类也不能写无参,只能调用有参。一般我们会把无参写上再接着写有参。
方法重写
-
重写都是方法的重写,与属性无关
-
子类重写父类的同名方法,参数列表相同
-
修饰符:范围可以扩大不能缩小:public>protected>default>private,当然private不能重写
-
抛出异常:范围可以缩小不能扩大:ClassNotFoundException–>Exception(大)
子类方法与父类一致,方法体不同
为什么要重写:
- 父类功能子类不一定需要,或不一定满足
我们建一个子类A,父类B,并写同名方法:
public class A extends B{
public static void text(){
System.out.println("A");
}
}
public class B {
public static void text(){
System.out.println("B");
}
}
调用方法:
public class Main {
public static void main(String[] args) {
//方法的调用只和左边,定义的数据类型有关
A a = new A();
a.text();//A
//父类的引用指向了子类
B b = new A();
b.text();//B
}
}
删去A、B的static,发现左边都多了一个东西:
其实就代表重载
删掉A中方法,alt+insert选择方法重载,直接回车生成方法:
@Override//注解,有功能的注释
public void text() {
super.text();
}
继续执行主程序,输出BB,把super.text();改回System.out.println(“A”);,继续执行主程序,输出却变成了AA
所以我们可以补全刚刚的结论:
- 静态方法(有static):方法的调用只和左边,定义的数据类型有关
- 非静态方法:重写(比如B b = new A();b.text();)
注意重写的方法只能是public,换成private就不行了