Java基础

第一节 面向对象

一.类与对象

类与对象的概念

类是具有相同属性和行为的一类事物. 而对象指的是一个类里面实际的一个实际存在的个体. 例如, 动物是一个类, 而一条小狗则是属于动物的单个个体, 即为对象. 在java中, 从类中实例化一个对象的语法如下:

类名 对象 = new 类名();
例如: Student stu = new Student();
复制代码

成员属性和成员方法

成员方法指的是在类里面定义的非static修饰的方法, 都叫做成员方法(方法有普通和静态的区别, 后面会说到); 成员属性是指在类的里面, 方法的外面定义的非static修饰的属性, 叫做成员属性, 也叫做成员变量或者全局变量, 成员方法的结构如下所示:

访问修饰符 返回值数据类型 函数名([参数列表]){
    实现方法的代码;
    [return 变量或者值];
}
例如: 
public String getStudentName(int StudentId) {
    String studentName = null;
    if (StudentId == 1) {
        studentName = "张三";
    }
    return studentName;
}
复制代码

类和对象之间的关系有以下3点:

  • 从一个类中创建任何一个对象, 都具有该类的所有属性和方法
  • 一个类可以创建无限个对象
  • 类当中成员属性和成员方法都必须通过对象去调用

局部变量和全局变量

成员变量也就是全局变量, 而局部变量是指在方法中声明的变量. 在同一个类中, 不能存在两个相同的全局变量; 在同一个方法中, 不能存在两个相同的局部变量; 但是, 同一个类中可以存在相同的全局变量和局部变量, 使用时会遵循就近原则, 即使用该变量的语句离哪个变量近, 就会使用哪个变量, 例如:

// 成员变量
public int a = 1;
// 成员方法
public void print() {
    system.out.println("a的值为: " + a); // 此时由于输出语句离成员变量近, 所以会输出a的值为1
    int a = 2;
    system.out.println("a的值为: " + a); // 此时由于输出语句离局部变量近, 所以会输出a的值为2
}
复制代码

成员方法的重载

在同一个类中, 具有相同的方法名, 不同的参数列表, 这种现象就是方法的重载. 不同的参数列表有以下三种情况:

  • 参数的个数不同
  • 参数的数据类型不同
  • 参数的数据类型顺序不同

创建对象内存分析

  • 栈内存: 方法的执行时会压入栈中, 在执行完毕后会从栈中弹出销毁.
  • 堆内存: 每次实例化一个对象的时候, 都会在堆内存中开辟一块空间, 该空间中存在对应类中的所有成员变量和成员方法, 并且成员变量有默认值.
  • 方法在执行时, 会将操作的对象压入栈中, 这个对象实际上是一个引用, 指向堆内存中的地址. 在Java中, 栈内存的一个对象只能指向一个堆内存中的地址, 而堆内存中的地址, 可以同时被多个栈内存中的对象指向.

值传递和引用传递

在java中的=代表的是赋值, 即将一个值给到一个变量. 赋值分为两种, 一种是值传递, 一种是引用传递; 对于基本数据类型的赋值就是值传递, 即直接把值赋值给变量. 而对于类类型和数组类型, 则是引用传递, 在将引用类型赋值给变量时是将引用类型的地址赋值给变量.例如:

int a = 1;
int b = a; // 此时是直接将a的值1赋值给变量b, b的值为1.
Student stu1 = new Student("小明","男"); // 对于stu1, 它保存的是指向堆内存中的Student("小明","男")对象的引用, 而非对象本身.
Student stu2 = stu1; // 此时是把stu1的引用赋值给stu2, stu1和stu2同时指向同一个对象.
复制代码

静态修饰符

static关键字可以用于修饰成员方法和成员属性, 被修饰的成员方法和成员属性会在类加载的时候就进行初始化, 所以在调用它们的时候可以不需要创建对象, 而是直接通过类名.方法名或者类名.属性名来进行调用. 在静态成员方法中, 不能直接调用非静态的成员方法或者非静态的成员属性, 因为静态成员方法优先与对象存在, 此时非静态成员方法和非静态成员属性还没有初始化, 会调用出错.

二.封装

封装的概念

封装是指在编写代码的过程中, 将代码的实现细节对外隐藏起来, 指提供公共的访问方式或者方法, 来操作其内部的细节或者属性. 封装的作用就是为了保护属性和数据的安全.

Java中封装的实现

在Java中, 封装的实现主要是通过几大关键字来实现的, 通过在类或者方法或者属性前添加者几个关键字, 可以达到封装的目的, 这几个关键字又叫做访问修饰符, 分别如下:

  • public: 公共的, 访问权限最大, 所有类都可以访问;
  • protected: 受保护的, 不同包下面的子类可以使用;
  • default: 不写任何访问修饰符就是default修饰的, 不同包就不可用;
  • private: 私有的, 只有自己可以使用.

由于的特点, 产生了一种比较特殊的类, 在这种类中通常只有成员属性和其对应的get/set方法, 这种类被称为失血模型, 例如:

public class Person{
    //成员变量
    private int age;
    //成员方法
    public void setAge(int a){
        age = a;
    }
    public int getAge(){
        return age;
    }
}
复制代码

对于一个成员变量, 我们通常将它对应的set方法中的参数写为与它同名的, 但是这个时候就有问题了, set方法的作用是将参数的值赋值给成员变量, 但是如果它俩同名, 我们怎么去确定是将谁赋值给谁呢? 这个时候就需要另外一个关键字了, 这个关键字叫做this, this代表的是当前调用这个属性或者方法的对象自己. 所以可以用它来区分成员变量和局部变量, 带this的就是成员变量, 所以上述失血模型在优化后的写法如下:

public class Person{
    //成员变量
    private int age;
    //成员方法
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge(){
        return this.age;
    }
}
复制代码

构造方法和构造代码块

在类里面有一种特殊的方法, 这种方法没有返回值, 方法名和类名相同, 这种方法被称为构造方法, 如下所示:

public class Student {
    private int a = 1;
    // 构造方法
    public Student() {}
    // 构造代码块
    {}
}
复制代码

在每次创建对象时, 对应的构造方法都会被调用. 构造方法可以不写出来, java在编译的时候会提供默认的无参构造方法, 但是, 如果手动添加了构造方法(无参或者有参), java将不再提供默认的无参构造方法, 所以在手动添加了有参构造方法时, 无参构造将不被提供, 如果需要使用, 就需要手动添加上去. 构造代码块是在构造方法之前执行的, 每次构造方法被调用时, 先执行构造代码块中的代码, 然后再执行构造方法中的代码.

三.继承

继承的概念

在java中继承使用extends关键字, 其使用方法如下所示:

public class Person {
    public int age;
}
public class Student extends Person {
    public String name;
}
复制代码

通过继承, 子类就拥有了父类的所有属性和方法, 例如上述例子, Student就拥有了父类的age属性. 并且子类还可以拥有自己的属性和方法, 例如上例的name属性, 就是Student类自己的属性. 由此可见, 继承的主要作用就是代码复用和扩展. 继承的主要特点有如下几点:

  • 子类拥有父类的所有属性和方法(包括私有的属性和方法), 但是父类的私有属性和私有方法子类是无法直接访问的, 只是拥有; 实际上这点与继承的原理有关系, 子类继承父类, 在创建子类对象时, 会先在开辟好的空间中初始化父类的对象, 然后再初始化子类, 子类持有父类, 所有子类可以直接调用父类的方法, 但是父类私有的属性和方法, 子类自己扩展的方法是不可以调用的, 只可以通过父类的普通方法(也就是所谓”继承下来”的方法)去进行调用, 其原理如下图所示:

继承.png

  • 子类可以拥有自己的属性和方法, 即子类可以对父类进行拓展;
  • 子类可以用自己的方式实现父类的方法.

super关键字

在每一个构造方法的第一行, 默认都是super(), 作用是调用父类的构造方法, 如果没有写super(), java会在编译的时候默认给出无参的super(), 调用父类的无参构造. super本质上是一个对象, 指父类对象, 所以可以在子类中使用super来调用父类的方法, 并且super只有在继承继承关系情况下, 子类中才可以使用.

方法重写

在继承关系中, 子类与父类具有与父类完全一样的方法, 这种被称为重写. 如果子类重写了父类的方法, 那么在创建子类对象后, 子类对象调用的将是自己类中重写后的方法.

  1. 重写的目的: 重写的主要目的是因为如果父类中的某些方法需要被调用, 但是其实现的效果并不是子类想要的, 那么子类自己定义一个和父类一样的方法, 实现自己的代码;
  2. 重写和重载的区别: 重写是发生在继承或者实现关系中, 子类重写父类的方法, 子类的方法除方法体以外都需要跟父类的方法一致; 重载是在同一个类中, 具有相同函数名, 不同的参数类型.

抽象

在java中, 抽象可以有抽象类, 也可以有抽象方法.

  1. 通过在类前面添加abstract关键字, 这个类就变成了抽象类. 抽象类的作用, 主要是用于规范子类行为的, 如果一个子类继承了一个抽象类, 那么它必须去重写抽象类中的所有抽象方法, 或者让它自己也变成一个抽象类; 抽象类有构造函数, 但是不能够new对象; 并且抽象类中是可以存在普通成员方法和成员变量的, 由于不能够new对象, 所以抽象类中的普通成员方法和成员变量只能够通过其子类对象去进行调用.
  2. 通过在方法前面添加abstract关键字, 并且不编写该方法的主题, 那么这个方法就变成了一个抽象方法. 如果一个类中有抽象方法, 那么这个类必须也变成抽象类.

四.多态

接口

在java中除了普通类, 抽象类意外还有一种比较特殊的类型, 这种类型被称作接口. 接口的声明使用interface关键字. 其具有如下特点:

  • 接口与类之间只能实现关系, 使用implements关键字实现;
  • 接口中的方法都是抽象方法, 所有的方法都不能存在方法体, 并且默认添加abstract关键字, 可以省略;
  • 一个类可以同时实现多个接口, 如果实现了多个接口, 那么该子类必须重写所有接口中的所有方法;
  • 接口中没有构造方法, 所以不能得到对象(与抽象类不同的是, 抽象类的构造方法会在创建其子类对象时调用, 从而得到对象并被子类对象所持有, 从而子类对象可以调用到抽象类中的普通成员方法和成员变量);
  • 接口中可以存在变量, 变量被fianl关键字修饰, 一旦被赋值以后不能再被修改.

final关键字

java中的final关键字可以用来修饰类、变量以及方法, 其主要作用如下:

  • final修饰类, 这个类被称为最终类, 不能再被继承;
  • final修饰变量, 被修饰的变量可以进行一次赋值, 这个赋值可以在声明的时候赋值, 也可以在声明后进行赋值, 但是如果一旦被赋值, 便不能再被修改;
  • final修饰方法, 这个方法便不能被重写, 也被称为最终方法, 由此可以看出, final跟abstract关键字是不能够一起使用的, 因为二者一个是必须重写方法, 一个是不允许方法被重写, 二者是冲突的.

多态的概念

多态是指一种事物具备多种形态, 在java中的表现形式就是父类的声明指向子类的实例, 如下所示:

父类声明 变量名 = new 子类实例();
Animal animal = new Cat();
复制代码

如上形式也被称为向上转型, 即将子类的实例转换为父类对象; 既然存在向上转型, 那么必然也存在向下转型, 向下转型的意思即将父类对象转换为子类对象, 前提是这个父类对象本身就是由子类对象向上转型而来的, 如下所示:

Animal animal = new Cat();    // 前提是这个对象本来就是子类的实例, 才能向下转型成功
子类的声明 变量名= (子类名)对象;
Cat c = (Cat)animal;
复制代码

那么那么在多态的情况下, 如果有一个对象是父类的声明指向子类的实例, 在使用这个对象调用方法的时候, 假如我要调用一个属性或者方法, 这个属性或者方法在父类或者子类中都存在, 那么我到底是调用的谁的方法呢? 这个时候就涉及到另一个概念了, 这个概念叫做动态绑定, 动态绑定主要的原则有以下四点

  • 该对象调用普通成员方法的时候, 编译的时候看左边, 运行时候先看右边;
  • 该对象调用静态成员方法的时候, 编译看左边, 运行看左边;
  • 该对象调用普通成员变量的时候, 编译看左边, 运行看左边;
  • 该对象调用静态成员变量的时候, 编译看左边, 运行看左边.

匿名对象

在实际的代码中, 有些对象我们可能只需要使用一次便不再需要了, 这个时候将新建对象的代码写全的话就没有必要了, 可以直接使用匿名对象, 省去繁复的代码编写, 具体操作如下:

public class Cat{
    public String color="橘猫";
}
public class Test{
	public static void main(String[] args){
        new Cat();//匿名对象
        System.out.println(new Cat().color);
    }	
}
复制代码

内部类

定义在其他类内部的类, 被称为内部类. 包含内部类的类被叫做外部类. 内部类可以直接访问外部类的成员变量, 包括阿私有的; 而外部类如果想要访问内部类的成员变量, 则必须先创建对象. 根据内部类在外部类中的位置, 将内部类分为两种, 分别是成员内部类和局部内部类.

  • 成员内部类
示例代码:
class Outer {
    //成员变量
    private int num = 10;
    //成员内部类
    class Inner {
        public void show() {
            System.out.println(num);
        }
    }
}
复制代码

成员内部类创建对象的语法为: 外部类名.内部类名 对象名 = new 外部类().new 内部类();; 如果是用private修饰了成员内部类, 那么内部类将不能在外部类以外再创建对象; 如果是使用了static关键字修饰了这个成员内部类, 则在获取这个内部类的对象时可以直接通过外部类.内部类 对象名 = new 外部类.内部类(), 该内部类如果需要直接访问外部类中其他成员属性, 那么这个这个属性必须也是static修饰的, 如果需要访问这个内部类中的静态属性或者静态方法, 则可以通过外部类.内部类.属性/方法();

//示例代码
class Outer {
    private int num = 10;
    private static int num2 = 100;
    // 内部类用静态修饰是因为内部类可以看作是外部类的成员
    public static class Inner { // 内部类用静态修饰,为静态内部类
        public void show() {
            // System.out.println(num); 不能访问,因为num是非静态的
            System.out.println(num2);
        }
        public static void show2() {// 静态内部类中的静态方法
            System.out.println(num2);
        }		
    }
}

class InnerClassDemo {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer.Inner();
        oi.show();
        oi.show2();
        //show2()的另一种调用方式
        Outer.Inner.show2();
    }
}
复制代码
  • 局部内部类

定义在方法内部的类被称为局部内部类, 局部内部类只能够在定义的方法内部使用, 并且局部内部类如果需要访问方法中定义的局部变量, 那么这个变量必须被final修饰, 这样做的目的是为了防止匿名内部类在使用这个变量的时候, 由于外部局部变量发生改变, 而导致结果与预期不一致甚至产生异常, 使用final修饰以后, 外部局部便令便不能够再被改变, 也就保证了结果的一致性.

//定义在局部位置的类
class Outer {
    private int num  = 10;
    public void method() {
        int num = 20;
        // final int num2 = 20;
        class Inner {// 内部类定义在方法的内部,为局部内部类
            public void show() {
                // System.out.println(num); 不能访问到局部变量num, 因为num甚至有可能不存在.
                // 从内部类中访问本地变量num2; 需要被声明为最终类型
                System.out.println(num2);//20
            }
        }
        //System.out.println(num2);
        Inner i = new Inner();// 局部位置可以new内部类的对象,并调用方法
        i.show();
    }
}
复制代码
  • 匿名内部类

匿名内部类是属于局部内部类的一种, 其本质上是继承了该类或者实现了该接口的子类匿名对象. 其使用方法如下所示:

前提:存在一个类或者接口
格式:
    new 类名或接口名(){
        重写方法;
    }
匿名内部类的本质:是一个继承了该类或者实现了该接口的子类匿名对象

//匿名内部类示例, 在控制台中输出"HelloWorld"
interface Inter{
    void show();
}
class Outer{
    public staitc Inter method(){
        return new Inter(){
        	public void show(){
                System.out.println("HelloWorld");
            }
    	}
    }
}
class OuterDemo{
    public static void main(String[] args){
        Outer.method().show();
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享