Java核心——面向对象
面向对象
面向对象:将功能封装进对象,强调具备了功能的对象,面向对象是基于面向过程的
面向对象是一种思想,能让复杂问题简单化,不需要了解具体的实现过程,只需要指挥对象去实现功能。
面向对象的优点:
- 可重用性:代码重复使用,减少代码量,提高开发效率。(继承、封装、多态)、
- 可扩展性:新的功能可以很容易的加入到系统中来,便于软件的修改。
- 可管理性:能够将功能与数据结合,方便管理。
面向对象的基本特征:
封装、继承、多态
面向对象的过程:
找对象、建立对象、使用对象、维护对象的关系
成员变量和局部变量
作用范围:
成员变量作用于整个类中,局部变量作用于方法中或语句中。
在内存中的位置:
成员变量在堆内存中,因为对象的存在才在内存中存在,局部变量存在栈内存中。
继承
子类拥有父类的全部特征和行为,Java只支持单继承。
继承是为了将多个类的通用属性和方法提取出来,放在父类,然后只需要在子类中各自定义自己独有的属性和方法,并以继承的形式在父类中获取它们的通用属性和方法。
封装
指隐藏对象的属性和实现细节,仅对外提供公共访问方式。(在使用对象时,没有必要知道对象内容是如何完成对应功能的,只需要指挥对象去执行即可),封装的目的在于保护信息。
封装的好处:
将变化隔离:内部功能细节变化不影响使用
便于使用:不用了解内部的具体实现
提高重用性
提高安全性:只对外暴露一些简单的内容供使用
封装的原则:
将不需要对外提供的内容都隐藏起来,把属性都隐藏起来,只提供公共方法对其访问。
函数本身就是最简单的封装体,类也是,可通过权限修饰符进行隐藏,包也是。
如上图,如果年龄为负数就会有安全隐患,所以可以使用修饰符控制权限。private int age;私有化后,类以外即使建立对象也不能直接访问该属性,但一旦私有,就需要对外提供访问方式(即getter和setter等)。
对外提供访问方式可以在这种访问方式中加入逻辑判断等语句,提高代码的健壮性。
私有仅仅是封装的一种表现形式,只要权限在别人访问不到的范围都是封装。
多态
多态性,即“一个接口,多个方法”。是同一个行为具有多个不同表现形式或形态的能力。是同一个接口使用不同的实例而执行不同操作。
多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。多态性允许一个接口被多个同类使用,弥补了单继承的不足。
多态的两种表现形式:重写(不同类中);重载(一个类中)
多态存在的三个必要条件:继承,重写,父类引用指向子类对象: Parent p = new Child();
多态的实现方式:重写;接口;抽象类和抽象方法
//多态实例:
abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
public void eat(){
System.out.println("eat fish");
}
public void work(){
System.out.println("catch mouse");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("eat bone");
}
public void work(){
System.out.println("look home");
}
}
public class Test{
public static void main(String[] args){
show(new Cat()); //以Cat对象调用show方法
show(new Dog()); //以Dog对象调用show方法
Animal a = new Cat(); //向上转型
a.eat(); //调用的是Cat的eat
Cat c = (Cat) a; //向下转型
c.work(); //调用的是Cat的work
}
public static void show(Animal a){
a.eat();
if(a instanceOf Cat){
Cat c = (Cat)a;
c.work();
}else if(a instanceOf Dog){
Dog c = (Dog)a;
c.work();
}
}
}
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态和继承的区别
-
继承
父类中private的属性和方法是父类的隐私,子类虽然继承,也没有这些属性和方法的引用,相当于没有继承。
子类变量会被优先调用,子类方法会覆盖父类方法。
同名情况下,子类都先使用自己的。
-
多态
编译时,只能调用父类中的方法,子类的特有方法无法访问,子类是父类的对象。
运行时,如果父类和子类中有相同的方法时,调用的是子类的方法,而不是父类的。
子类方法会覆盖父类方法。
编译看父类,运行看子类。
父类变量会优先调用,父类使用自己的变量,使用子类的方法。
Animal dog = new Dog();
-
使用继承一般是为了复用性,为了重写父类方法
使用多态一眼是为了可维护性,为了同一个引用调用不同子类的特有同名方法
对象类型转换:向上转型和向下转型
Java中引用类型之间的类型转换(父子关系的两个类)主要有两种,向上转型upcasting和向下转型downcasting
-
向上转型
父类引用指向子类对象,把子类对象直接赋给父类引用
使用向上转型可以调用父类类型中的所有成员,不能调用子类类型中特有成员
fatherClass obj = new sonClass();
-
向下转型
sonClass obj = (sonClass) fatherClass;
类型强制转换时想运行成功就必须保证父类引用指向的对象一定是该子类对象,最好先使用instanceof判断:
Animal animal = new Cat(); if(animal instanceof Cat){ Cat cat - (Cat)animal; ... } -
instanceof关键字
判断一个对象是否为一个类的实例
boolean result = obj instanced Class;
super与this关键字
-
super关键字:实现对父类成员的访问,用来引用当前对象的父类
this.方法名:指向自己的引用。
⚠️static修饰的方法不能使用this引用
class Animal{
void eat(){
System.out.println("animal : eat");
}
}
class Dog extends Animal{
void eat(){
System.out.println("dog : eat");
}
void eatTest(){
this.eat(); //this调用自己的方法
super.eat(); //super调用父类方法
}
}
public class Test{
public static void main(String[] args){
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
-
this.属性名
哪个对象在调用this所在的函数,this就代表哪个对象。
当一个类的属性(成员变量)名与访问该属性的方法参数名相同时,需要使用this关键字来访问类中的属性,以区分类的属性和方法中的参数。
class Person{
private String name;
private int age;
Person(String name){
this.name = name;
}
Public void speak(){
System.out.println("name = " + name);
}
}
class PersonDemo{
public static void main(String[] args){
Person p = new Person("Lisa"); //执行到这里,this代表p引用
Person p1 = new Person("Jennie"); //执行到这里,this代表p1引用
}
}
-
this():访问构造方法
⚠️this()只能写在构造方法中,并且在构造方法中必须是第一条语句
public class Student{
String name;
//无参构造方法
public Student(){
this("Lisa");//使用this()调用构造方法给name赋值
}
//有参构造方法
public Student(String name){
this.name = name;
}
public void print(){
System.out.println("name = " + name);
}
public static void main(String[] args){
Student stu = new Student();
stu.print();
}
}
重写override和重载overload
-
重写
子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。
class Animal{ public void move(){ System.out.println("Animals can move."); } } class Dog extends Animal{ public void move(){ System.out.println("Dogs can move."); } public void bark(){ System.out.println("Dogs can bark.") } } public class TestDog{ public static void main(String args[]){ Animal a = new Animal(); //Animal对象 Animal b = new Dog(); //Dog对象 a.move(); b.move(); b.bark(); //这句话会报错,因为b的引用类型Animal没有bark方法 } }- 参数列表必须完全相同
- 返回类型可以不相同,但是必须是父类返回值的派生类
- 访问权限不能比父类中被重写的方法的权限更低
- final方法不能被重写
- static方法不能被重写,但是能够被再次声明
- 构造方法不能被重写
- 不继承不能重写
-
重载
overload是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。
public class Overloading{ public int test(){ System.out.println("test1"); return 1; } public void test(int a, String s){ System.out.println("test2"); } public void test(String s, int a){ System.out.println("test3"); } }
抽象类abstract
一个类中没有足够的信息来描绘一个具体的对象,这个类称为抽象类
- 抽象方法没有方法体,必须在抽象类中
- 抽象类中可以有具体方法
- 子类重写父类时,必须重写父类所有的抽象方法
- 抽象类不能实例化,即不能使用new关键字创建对象
public abstract class Shape{
public int width;
public int height;
public Shape(int width, int height){
this.width = width;
this.height = height;
}
public abstract double area();
}
public class Square extends Shape{
public Square(int width, int height){
super(width, height);
}
@Override
public double area(){
retrun width * height;
}
}
构造函数
-
构造函数特点:
函数名与类名相同
不用定义返回值类型
不可以写return语句
-
作用:
给对象进行初始化
-
构造函数对象一建立就会调用与之对应的构造函数,可用于给对象进行初始化,当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数,当自己定义了构造函数后,默认的空构造函数就不存在了。
-
多个构造函数是以重载的形式存在的。
构造函数示例👇
class Person{
Person(){
System.out.println("Person run");
}
}
class PersonDemo{
public static void main(String[] args){
Person p = new Person();
}
}
构造代码块👇
{
System.out.println("person code run");
}
-
构造代码块中,对象一建立就立即运行,而且优先于构造函数执行。构造代码块中定义的是不同对象具有共性的初始化内容。
-
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化;而构造函数是给对应的对象初始化。
接口
接口由全局常量和公共的抽象方法组成。
⚠️接口没有构造方法,不能被实例化
接口可以继承其他接口。子接口可以对父接口的方法和常量进行重写
public interface MyInterface{ //具有public的接口允许任何类使用,没有public的将局限于所属的包
String name; //不合法,变量name必须初始化
int age = 20; //合法,等同于public static final int age = 20;
void getInfo(); //方法声明,等同于public abstrct void getInfo();
}
一个类可以实现多个接口,用implements关键字,也是Java为单继承灵活性不足所做的补充
⚠️implements必须放在extends部分之后
内部类
在类的内部定义另一个类,内部类Inner,外部类Outer(最外层的类叫顶层类)
内部类可以很好的实现隐藏,内部类拥有外部类的所有元素的访问权限

public class Test{
public class InnerClass{ //内部类与外部类不能重名
public int getSum(int x, int y){
return x+y;
}
}
public static void main(String[] args){
Test.InnerClass ti = new Test().new InnerClass();
//在外部类中可以直接通过InnerClass ic = new InnerClass()访问
int i = ti.getSum(2, 3);
System.out.println(i);
}
}
-
实例内部类
在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例
在实例内部类中,可以直接访问外部类的所有成员
外部类必须通过内部类的实例访问内部类的成员
-
静态内部类
static class Inner{}
public class Outer{ int a1 = 0; //实例变量 static int b1 = 0; //静态变量 static class Inner{ int a = 0; //实例变量 static int b = 0; //静态变量 Outer o = new Outer; int aa = o.a1; //访问 int bb = b1; //访问 } } class OtherClass{ Outer.Inner oi = new Outer.Inner();//在创建静态内部类的实例时,不需要创建外部类的实例 int a2 = oi.a; //访问 int b2 = Outer.Inner.b; //访问 } -
局部内部类
public class Test{ int a = 0; int c = 0; public void method(){ int b = 0; final int c = 0; class Inner{ //局部内部类不能使用访问控制修饰符和static修饰符 //不能定义static成员 int a2 = a ; //可以访问外部类的所有成员 //int b2 = b;编译出错 int c2 = c; //只能访问当前方法中final类型的参数与变量 int c3 = Test.this.c; //访问外部类中的同名成员 } Inner i = new Inner();//只在当前方法中有效 } }

浙公网安备 33010602011771号