面向对象
要理解面向对象思想,我们先要知道什么是对象?
《Java编程思想》中提到“万物皆为对象”的概念。它将对象视为一种奇特的变量,它除了可以存储数据之外还可以对它自身进行操作。它能够直接反映现实生活中的事物,例如人、车、小鸟等,将其表示为程序中的对象。每个对象都具有各自的状态特征(也可以称为属性)及行为特征(方法),java就是通过对象之间行为的交互来解决问题的。
面向对象就是把构成问题的事物分解成一个个对象,建立对象不是为了实现一个步骤,而是为了描述某个事物在解决问题中的行为。
而类是从对象的属性和行为特征抽象出来的,对象是类的一个实例。比如说马,马是指一个概念,你可以具体到某一匹马,那么,这匹马就是马类的一个对象
创建类的格式
class 类名(首字母大写){
属性
方法
}
创建对象并给属性赋值,调用函数
类名 变量名 = new 类名();
变量名.属性 = 变量;
变量名.方法();
class Person{
String name;
int age;
public void run(){
System.out.println("奔跑");
}
}
创建一个人的类,具有名字和年龄属性,还具有奔跑行为
public static void main(String[] args) {
Person per = new Person();
per.name = "里斯";
per.age = 18;
per.run();
}
在main方法中创建对象,并赋值,调用函数
一、类具有三大特征:封装,继承,多态
1.封装
场景需求:试想一下,当你给一个对象赋值-5岁的时候,这在现实中是不可能的,但是在程序中是成功执行。
总结一下:核心思想就是“隐藏细节”、“数据安全”,将对象不需要让外界访问的成员变量和方法私有化,只提供符合开发者意愿的公有方法来访问这些数据和逻辑,保证了数据的安全和程序的稳定。所有的内容对外部不可见。
于是,我们将类的属性私有化(private),在内部进行处理,不允许外部通过赋值的方法进行修改,可以通过调用方法进行修改,方法内部进行一定处理,即可达到要求。
封装性的体现:
- Java规定的4种权限(从小到大排列):private、缺省(不加修饰符)、protected、public
- 4中权限可以用来修饰累计类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省,public
封装举例
private int age;
public void setAge(int age){
if ( this.age >0 && this.age < 150){
this.age = age;
}else {
System.out.println("不符合要求");
}
}
定义变量age只有小于150大于0才能赋值成功
2.继承
场景:有一个类有很多满足你的要求,你是要把这个类赋值下来吗?不需要赋值,使用继承,就拥有了他的大部分功能。
总结:子类可以继承父类的属性和方法,并对其进行拓展。将其他的功能继承下来继续发展 。
格式
class A extends B{}
A:子类,派生类,subclass
B:父类,基类,superclass
继承举例
public class Demo01 extends Person{
public static void main(String[] args) {
Demo01 de = new Demo01();
de.name = "li";
de.print();
}
}
class Person{
String name;
public void print(){
System.out.println(name);
}
}
注意:一旦父类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。特别的,子类中声明为private的属性或方法,子类继承父类之后,仍然认为获取了父类中私有的结构,只有因为封装性的影响,使得子类不能直接调用父类的结构而已
规定
- 一个类可以被多个子类继承。
- Java中类的单继承性,一个类只能有一个父类
- 父子类是相对的概念。
- 子类继承父类之后,就获取了直接父类以及所有简介父类中声明的属性和方法
如果我没有显示的声明一个类的父类的话,则此类继承于java.lang.Object类
3.多态
理解多态性:可以理解为一个事物的多种形态
何为多态性:对象的多态性,父类的引用指向子类的对象(或子类的对象赋给父类的引用)
多态性的使用前提:
- 类的继承关系
- 父类方法的重写
格式:父类类型 变量名=new 子类类型();
多态举例:
public class Demo01 extends Person{
@Override
public void print() {
System.out.println("Demo01");
}
public static void main(String[] args) {
Person per = new Demo01();
per.print();
}
}
class Person{
String name;
public void print(){
System.out.println(name);
}
}
虚拟方法调用(多态情况下):子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法成为虚拟方法,父类根据赋给它的不同子类对象,动态动用属于子类的该方法,这样的方法调用在编译器是无法确定的
运行时类型:在运行时才知道行为,例如随机数,只有运行时才知道是多少。
编译式类型:不运行就知道干啥
向下转型:有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能使用.。因此可以把多态化的这个对象转为子类对象,调用子类属性和方法
二、构造器
首先构造器在创建类的对象时就出现了,就是类后面的括号。
用处:在创建对象时,初始化类的属性。
格式:权限修饰符 类名(形参列表){}
举例:
public class Demo01{
public static void main(String[] args) {
Person p1 = new Person("李四",18);
}
}
class Person{
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
// 当初始化类信息之后,会覆盖原先的无参构造器,一般情况下,会重新协商
// 可以定义多个构造器,在调用时会根据参数不同调用不同的构造器,在一个构造器内部还可以调用其他构造器,在this中说明如何调用
public Person(){
}
}
注意:如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器。当你自己创建有参构造器之后,会覆盖原先的无参构造器
三、this关键字
用来区分类的属性与传进来的参数
格式:this.属性 = 形参
this调用构造器:
- 我们在类的构造器中,可以显示的使用“this(形参列表)”方式,调用本类中指定的其他构造器
- 构造器中不能通过“this(形参列表)”方式调用自己
- 规定:“this(形参列表)”必须声明在当前构造器的首行
- 构造器内部,最多只能声明一个“this(形参列表)”,用来调用其他构造器
举例:
class Person{
String name;
int age;
public Person(){};
public Person(String name){
this.name = name;
}
public Person(String name, int age){
// 调用其他构造器
this(name);
this.age = age;
}
}
四、super关键字
场景:当父类的方法被子类重写之后,还想要使用父类的这个方法,子父类出现同名的属性
super关键字的使用
- super理解为:父类的
- super可以用来调用:属性、方法、构造器
1、super的使用,调用父类属性,方法
- 我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略“super.”
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法以后,我们要想在子类中调用父类中声明的方法,则必须显式的使用“super.方法”的方式,表明调用的是父类中声明的方法。
举例
public class Demo01 extends Person{
public static void main(String[] args) {
Demo01 de = new Demo01();
de.print();
//父类的print
//子类的print
}
@Override
public void print() {
super.print();
System.out.println("子类的print");
}
}
class Person{
String name;
int age;
public void print(){
System.out.println("父类的print");
}
}
2.super调用构造器
- 我们可以在子类的构造器中显示的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
- “super(形参列表)”的使用,必须声明在子类构造器的首行
- 我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现
- 在构造器的首行,没有显式的声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中空参的构造器
- 在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器
当父类不存在空参构造器,而子类没有使用其他构造器时,就会报错
举例:
public class Demo01 extends Person{
char gender;
public Demo01(String name, int age, char gender){
// 调用父类构造器
super(name,age);
this.gender = gender;
}
}
class Person{
String name;
int age;
public Person(){};
public Person(String name, int age){
this.name = name;
this.age = age;
};
}
static关键字
用处:.static可以用来修饰:属性,方法,代码块,内部类。存在于方法区的静态域中
使用static修饰属性:静态变量
属性,按是否使用static修饰,分为:静态属性和非静态属性(实例变量)
静态属性:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改了的。
static修饰属性的其他说明:
- 静态变量随着类的加载而加载,可以通过”类.静态变量”的方式进行调用
- 静态变量的加载要早于对象的创建
- 由于类只会加载一次(创建完对象之后,会把类缓存到方法去),则静态变量在内存中也只会存在一份:存在方法区的静态域中
static修饰方法,静态方法
-
随着类的加载而加载,可以通过“类.静态方法”的方法进行调用
-
静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
举例:
public class Demo01{
public static void main(String[] args) {
// Person.name = "张三";
System.out.println(Person.name); // "张三"
Person.print();// "static"
Person p1 = new Person();
p1.name = "李四";
Person p2 = new Person();
System.out.println(p2.name); // 李四
}
}
class Person{
static String name = "张三";
int age;
static void print(){
System.out.println("static");
}
public void A(){
System.out.println("A");
}
}
static注意点
在静态的方法内,不能使用this关键字,super关键字
关于静态属性和静态方法的使用,大家都从生命周期的角度去理解
如何使用
开发中,如何确定一个属性是否要声明为static?
属性是可以被多个对象共享的,不会随着对象的不同而不同的
开发中,如何确定一个方法是否要声明为static?
操作静态属性的方法,通常设置为static
工具类中的方法,习惯上声明为static。比如:Math,Arrays,Collections
抽象方法概念:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计的非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
abstract:抽象的
abstract可以用来修饰的结构:类,方法
abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(设计:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
abstract修饰方法,抽象方法
-
抽象方法只有方法的声明,没有方法体
-
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的
-
若子类重写了父类所有的抽象方法后,此子类方可实例化
若子类没有重写父类中所有抽象方法,则子类一定是抽象类
抽象类与抽象方法
抽象类使用举例
public class Demo01 extends animal{
// 重写抽象方法
@Override
public void eat() {
System.out.println("Demo01");
}
}
//抽象类
abstract class animal{
abstract public void eat();
}
抽象类继承举例:
public class Demo01 extends Pig{
// 重写间接父类抽象方法
@Override
public void eat() {
System.out.println("Demo01");
}
// 重写父类抽象方法
@Override
public void run() {
}
}
//抽象类继承
abstract class Pig extends animal{
abstract public void run();
}
//抽象类
abstract class animal{
abstract public void eat();
}
通过单继承,继承父类,父类上面还有父类,并且是抽象类,需要重写全部抽象类的抽象方法。
注意:
- abstract不能用来修饰:属性,构造器等结构
- abstract不能用来修饰私有方法,静态方法,final的方法,final的类
接口
接口格式:interface 类名{}
如何定义接口
定义接口中的成员:
JDK7及以前:只能点定义全局常量和抽象方法
- 全局常量:public static final的,但是书写时,可以省略不写(但是照样有)
- 抽象方法:public abstract的
** JDK8**:除了定义全局常量和抽象方法之外,还可以定义静态方法,默认方法(略)
接口内部只能定义public static final属性,public abstract抽象方法,当你没有带上时,会自动帮你填上,
接口举例
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
// 多态调用
com.transferData(flash);
}
}
class Computer{
public void transferData(USB usb){//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法,默认方法
举例
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
// SubClass.method1();
// 知识点1:接口中定义的静态方法,只能通过接口调用
CompareA.method1();
// 知识点2.通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口的默认方法,调用时,仍然调用的是重写以后的方法 --->类优先原则
s.method2();
// 知识点3如果子类(或实现类)继承的父类和实现的接口中声明了同名同参的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参的方法 --》类优先原则
// 知识点4 如果实现类实现了多个接口,而这多个接口中定义了同名同参的默认方法,那么在实现类没有重写此方法的情况下,报错 --》接口冲突
s.method3();
}
}
class SuperClass{
public void method3(){
}
}
//class SubClass extends SuperClass implements CompareA,CompareB
class SubClass implements CompareA,CompareB{
//重写CompareA接口中的默认方法
@Override
public void method2() {
System.out.println("广州");
}
}
interface CompareA {
public static void method1(){
System.out.println("北京");
}
public default void method2(){
System.out.println("上海");
}
public default void method3(){
System.out.println("昆明");
}
}
interface CompareB{
default void method3(){
System.out.println("上海");
}
}
注意
- 接口中不能定义构造器的!意味着接口不可以实例化
- Java开发中,接口通过让类区实现(implements)的方式来使用
如果实现类覆盖了接口中的所有抽象方法,则此实现类可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类是一个抽象类 - 接口与接口之间可以继承,多继承
- Java中,接口和类是并列的两个结构
- 接口的具体使用,体现多态性
- Java类可以实现多个接口 —>摸不了Java但继承性的局限性
格式:class 类名 extends 父类 implements 接口1,接口2{}
接口与接口之间用英文逗号隔开
接口规范
接口,实际上可以看作是一种规范。体现了现实世界中”如果你是/要…则必须能…“的思想。继承是一个”是不是“的关系,而接口实现则是”能不能“的关系。
比如说:它给你说了你要做什么,第一,你要怎么,第二,你要怎么。然后怎么去做,是你的事,统一了任务。然后去完成。
所以,接口的只要用途就是被实现类实现(面向接口边框)