当前位置:网站首页 > Java基础 > 正文

java面向对象基础

一,类和方法属性的基本概念

1.1,修饰符的区分

访问修饰符

default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。 private : 在同一类内可见,子类不可见。使用对象:变量、方法。 注意:不能修饰类(外部类) public : 对所有类可见。使用对象:类、接口、变量、方法 protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。 

非访问修饰符

static 修饰符,用来修饰类方法和类变量。 final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。 abstract 修饰符,用来创建抽象类和抽象方法。 synchronized 和 volatile 修饰符,主要用于线程的编程。 

比如说我们定义了一个名为Person类:

public class Person { 
    public String name;// public int age;// } 

注意它的属性name和age,是public修饰的,这说明该属性是公共的,对所有类可见,我们可以在类外直接访问和修改它.例如,我可以在HelloWorld类中对它进行访问和修改:

public class HelloWorld { 
    public static void main (String[] args){ 
    Person ming = new Person(); ming.name = "Xiao Ming"; ming.age = -99; // age设置为负数 System.out.println(ming.name); } } 

而如果我们想要有更好的封装,让定义的属性只能够在类内进行访问和修改,就需要使用private来创建私有属性.

class Person { 
    private String name; private int age; public String getName() { 
    return this.name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return this.age; } public void setAge(int age) { 
    if (age < 0 || age > 100) { 
    throw new IllegalArgumentException("invalid age value"); } this.age = age; } } 

使用时:

public class HelloWorld { 
    public static void main (String[] args){ 
    Person ming = new Person(); ming.setAge(100); ming.setName("猪八戒"); System.out.println(ming.getName()); System.out.println(ming.getAge()); } } 

这样一来,外部代码就没有任何机会把age设置成不合理的值。

1.2,方法的修饰和this

对于属性变量可以利用public和private来修饰,区别是否可以类外直接访问,那么对于方法,也是如此.

public class Person { 
    private String name; public String getName() { 
    this.pringName(); return this.name; } public void setName(String name) { 
    this.name = name; } private void pringName(){ 
    System.out.println(this.name); } } 

可以看到,私有的只能够在自己类中进行访问.而this,就是只想当前实例,用来访问当前类的属性和方法.

1.3,方法的参数

因为java是强类型的,所以参数的传递必须按照初始设定时的顺序和类型以及数量进行传参.
但是需要注意的是,方法的传参需要注意是基本类型的传参还是引用类型的传参.
基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。
引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)。
可以这么理解:方法的传参都是通过复制值的方式进行,对于基本类型,复制值传入,所以后续修改不影响,而对于引用类型,传入的是地址值的复制,所以后续修改引用类型数据的值,因为地址没改变,所以传入的值发生了变化.

Person p = new Person(); String[] fullname = new String[] { 
    "Homer", "Simpson" }; p.setName(fullname); // 传入fullname数组 System.out.println(p.getName()); // "Homer Simpson" fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart" System.out.println(p.getName()); // "Bart Simpson"? 

1.4,构造方法

class Person { 
    private String name; private int age; public Person(String name, int age) { 
    this.name = name; this.age = age; } public String getName() { 
    return this.name; } public int getAge() { 
    return this.age; } } 

使用的时候:

Person p = new Person("Xiao Ming", 15); System.out.println(p.getName()); System.out.println(p.getAge()); 

二,方法的重载

同一个类中,方法名相同,但各自的参数不同,称为方法(Overload),方法重载的目的是相同功能的方法,采用一样的名字,开发人员更好记.

class Person { 
    public void hello() { 
    System.out.println("Hello, world!"); } public void hello(String name) { 
    System.out.println("Hello, " + name + "!"); } public void hello(String name, int age) { 
    if (age < 18) { 
    System.out.println("Hi, " + name + "!"); } else { 
    System.out.println("Hello, " + name + "!"); } } } 

那编译器又是如何区分这些重载的方法的呢?其实就是调用方传入参数来区分.不同的重载方法在被调用的时候,传入的参数是不一样的.

三,方法的继承

因为js中也有继承,这个就很好理解,就是把共有的基本属性方法放到顶层类中去,这样子类只需要继承该属性方法就可以直接使用,而没必要重新定义了,可以提高代码的简洁性.

3.1,使用extends进行继承

class Person { 
    private String name; private int age; public String getName() { 
    return this.name; } public void setName(String name) { 
    this.name=name; } public int getAge() { 
    return this.age; } public void setAge(int age) { 
    this.age=age; } } class Student extends Person { 
    // 不要重复name和age字段/方法, // 只需要定义新增score字段/方法: private int score; public int getScore() { 
    return this.score; } public void setScore(int score) { 
    this.score=score; } } 

使用的时候:

 Student p = new Student(); p.setName("孙悟空"); p.setAge(20); p.setScore(40); System.out.println(p.getName()); System.out.println(p.getAge()); System.out.println(p.getScore()); 

3.2,使用protected让子类能访问

class Person { 
    protected String name;//使用protected进行修饰,让子类能够访问 public void setName(String name) { 
    this.name=name; } } class Student extends Person { 
    public void printName(){ 
    System.out.println(this.name); } } 

3.3,使用super指代父类

第一种super的使用方式可以用this代替:

class Person { 
    protected String name;//使用protected进行修饰,让子类能够访问 public void setName(String name) { 
    this.name=name; } } class Student extends Person { 
    public void printName(){ 
    System.out.println(super.name);//使用super指代父类,相当于this } } 

在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();

class Person { 
    protected String name; protected int age; public Person(String name, int age){ 
    this.age=age; this.name=name; } } class Student extends Person { 
    protected int score; //构造方法中自动调用 public Student(String name, int age, int score) { 
    super(name, age); // 自动调用父类的构造方法 this.score = score; } public void printInfo(){ 
    System.out.println(this.name); System.out.println(this.age); System.out.println(this.score); } } 

可以看到,这里的super就是调用父类的构造方法.具体在实例化student的时候:

 Student p = new Student("孙悟空",20,80); p.printInfo(); 

3.4,向上转型

 Person p = new Student("孙悟空",20,80); p.test(); 

也可以这样进行转型:

Student s = new Student(); Person p = s; // upcasting, ok 

3.5,向下转型

同样的道理,把父类的实例转为子类的,就叫做向下转型,但这明显存在问题,因为子类具备的属性方法大概率父类不具备.

Person p2 = new Person(); Student s2 = (Student) p2; //报错 

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

Person p = new Person(); System.out.println(p instanceof Person); // true System.out.println(p instanceof Student); // false 

四,多态

4.1,子类方法的覆写

如果子类有和父类同名同参同返回的方法,则称为覆写.

class Person { 
    public void run() { 
    System.out.println("person类的"); } } class Student extends Person { 
    @Override public void run() { 
    System.out.println("student类的"); } } 

这里使用@Override来让编译器知道这是覆写,它就能直接用覆写的规则来检查代码了.

4.2,多态的概念

运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。这就是多态的特性.

class Person { 
    public void run() { 
    System.out.println("person类的"); } } class Student extends Person { 
    @Override public void run() { 
    System.out.println("student类的"); } } 

使用的时候:

 Person p = new Student(); p.run();//这里执行的是student的run方法,因为new的时候,指针指向了student 
package com.masiyi.servlet; public class Main { 
    public static void main(String[] args) { 
    // 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税: Income[] incomes = new Income[] { 
    new Income(3000), new Salary(7500), new StateCouncilSpecialAllowance(15000) }; System.out.println(totalTax(incomes)); } public static double totalTax(Income... incomes) { 
    double total = 0; for (Income income: incomes) { 
    total = total + income.getTax(); } return total; } } //普通津贴,税率0.1 class Income { 
    protected double income; public Income(double income) { 
    this.income = income; } public double getTax() { 
    return income * 0.1; // 税率10% } } //一般津贴,税率0.2 class Salary extends Income { 
    public Salary(double income) { 
    super(income); } @Override public double getTax() { 
    if (income <= 5000) { 
    return 0; } return (income - 5000) * 0.2; } } //特殊津贴,税率是0 class StateCouncilSpecialAllowance extends Income { 
    public StateCouncilSpecialAllowance(double income) { 
    super(income); } @Override public double getTax() { 
    return 0; } } 

可以看到,存在多个getTax,但是我们的totalTax仅仅需要和income打交道,它完全不需要知道Salary和StateCouncilSpecialAllowance的存在,就可以正确计算出总的税。如果我们要新增一种稿费收入,只需要从Income派生,然后正确覆写getTax()方法就可以。把新的类型传入totalTax(),不需要修改任何代码。
可见,多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。

4.3,final修饰的方法子类不允许覆写

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override:

class Person { 
    protected String name; public final String hello() { 
    return "Hello, " + name; } } class Student extends Person { 
    // compile error: 不允许覆写 @Override public String hello() { 
    } } 

4.4,使用final修饰的类不允许继承

如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final。用final修饰的类不能被继承:

final class Person { 
    protected String name; } // compile error: 不允许继承自Person class Student extends Person { 
    } 

对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改。例如:

class Person { 
    public final String name = "Unamed"; } 

这时候name就是不可修改的属性了.

五,抽象类

如果一个类,本身被设计成只能用于被继承,或者它的方法,只用来被子类覆写.

public class Main { 
    public static void main(String[] args) { 
    Person p = new Student(); p.run(); } } abstract class Person { 
    public abstract void run(); } class Student extends Person { 
    @Override public void run() { 
    System.out.println("Student.run"); } } 

当我们定义了抽象类Person,以及具体的Student、Teacher子类的时候,我们可以通过抽象类Person类型去引用具体的子类的实例:

Person s = new Student(); Person t = new Teacher(); 

这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person类型变量的具体子类型,因为肯定都有run函数(父类是抽象方法,子类是覆写的方法)

// 不关心Person变量的具体子类型: s.run(); t.run(); 
上层代码只定义规范(例如:abstract class Person); 不需要子类就可以实现业务逻辑(正常编译); 具体的业务逻辑由不同的子类实现,调用者并不关心。 

六,接口

在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
如果一个抽象类没有字段,所有方法全部都是抽象方法

abstract class Person { 
    public abstract void run(); public abstract String getName(); } 
interface Person { 
    void run(); String getName(); } 

因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

6.1,使用implements实例化接口

当一个具体的class去实现一个interface时,需要使用implements关键字。举个例子:

class Student implements Person { 
    private String name; public Student(String name) { 
    this.name = name; } @Override public void run() { 
    System.out.println(this.name + " run"); } @Override public String getName() { 
    return this.name; } } 

6.2,一个类可以实现多个interface

因为接口是纯粹的抽象类和抽象属性方法,于是一个类可以复合地实现多个接口,相当于取并集.

class Student implements Person, Hello { 
    // 实现了两个interface ... } 

6.3,interface继承自interface使用extends

接口可以被继承,接口的继承使用extends,相当于接口的扩展:

interface Hello { 
    void hello(); } interface Person extends Hello { 
    void run(); String getName(); } 

6.4,default方法

public class Main { 
    public static void main(String[] args) { 
    Person p = new Student("Xiao Ming"); p.run(); } } interface Person { 
    String getName(); default void run() { 
    System.out.println(getName() + " run"); } } class Student implements Person { 
    private String name; public Student(String name) { 
    this.name = name; } public String getName() { 
    return this.name; } } 

七,静态字段和静态方法

7.1,静态字段

上文创建的字段都是实例化后对象自身的,有些字段,我们希望所有实例化对象公用,也就是该字段实际上是超类中的,并且实例化的类的该字段都指向同一个,那么就可以用静态字段.使用static来修饰说明是静态字段.

public class Main { 
    public static void main(String[] args) { 
    Person ming = new Person("Xiao Ming", 12); Person hong = new Person("Xiao Hong", 15); ming.number = 88; System.out.println(hong.number); hong.number = 99; System.out.println(ming.number);//无论修改哪一个,两个实例对象的number都会变更,因为本质上是同一个,是Person.number } } class Person { 
    public String name; public int age; public static int number; public Person(String name, int age) { 
    this.name = name; this.age = age; } } 

实际上,只有一个静态字段number,也就是Person.number,我们一般不用实例.number的方式来写.我们一般这样写:

Person.number = 99; System.out.println(Person.number); 

因为它就是指向Person的.

7.2,静态方法

有静态字段,就也有静态方法.调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。

public class Main { 
    public static void main(String[] args) { 
    Person.setNumber(99); System.out.println(Person.number); } } class Person { 
    public static int number; public static void setNumber(int value) { 
    number = value; } } 
Arrays.sort() Math.random() 

7.3,接口的静态方法

因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:

public interface Person { 
    public static final int MALE = 1; public static final int FEMALE = 2; } 

实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:

public interface Person { 
    // 编译器会自动加上public statc final: int MALE = 1; int FEMALE = 2; } 

因为接口肯定最后要被实例化,它如果要定义字段,也只能定义常量字段,于是肯定是final.

八,包

 要特别注意:包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。 

8.1,包作用域

位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。例如,Person类定义在hello包下面:

package hello; public class Person { 
    // 包作用域: void hello() { 
    System.out.println("Hello!"); } } 

Main类也定义在hello包下面:

package hello; public class Main { 
    public static void main(String[] args) { 
    Person p = new Person(); p.hello(); // 可以调用,因为Main和Person在同一个包 } } 

8.2,import

我们使用import来引入其他包

// Person.java package ming; // 导入mr.jun包的所有class: import mr.jun.*; public class Person { 
    public void run() { 
    Arrays arrays = new Arrays(); } } 

如果两个包有相同的类名,那么,就得有类需要写全名了,避免冲突.

九,内部类

如果一个类定义在另一个类的内部,这个类就是Inner Class:

class Outer { 
    class Inner { 
    // 定义了一个Inner Class } } 

如下所示,内部类的实例化,需要依附于外部类:

public class Main { 
    public static void main(String[] args) { 
    Outer outer = new Outer("Nested"); // 实例化一个Outer Outer.Inner inner = outer.new Inner(); // 实例化一个Inner inner.hello(); } } class Outer { 
    private String name; Outer(String name) { 
    this.name = name; } class Inner { 
    void hello() { 
    System.out.println("Hello, " + Outer.this.name); } } } 

内部类的实例化:

Outer.Inner inner = outer.new Inner(); // 实例化一个内部类 
到此这篇java面向对象基础的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • java核心类2024-11-29 21:09:07
  • java异常的捕获2024-11-29 21:09:07
  • java泛型2024-11-29 21:09:07
  • java spring调用db.properties外部文件时出错2024-11-29 21:09:07
  • macbook安装低版本的jdk,提示“Oracle 的 Java 要求 Mac OS X 10.7.3 或更高版本”2024-11-29 21:09:07
  • java基础-(七)-springBoot项目的helloworld2024-11-29 21:09:07
  • java基础-(六)-使用 Spring Initializr 创建springBoot项目2024-11-29 21:09:07
  • java基础-(四)-Maven安装与配置2024-11-29 21:09:07
  • java基础-(三)-java基础语法2024-11-29 21:09:07
  • java基础-(二)-第一个java程序2024-11-29 21:09:07
  • 全屏图片