美文网首页Java-Python-Django社区程序员
【JavaSE(五)】Java面向对象(下)

【JavaSE(五)】Java面向对象(下)

作者: 苍云横渡 | 来源:发表于2018-05-12 13:08 被阅读33次

原文地址:https://www.cloudcrossing.xyz/post/37/

1 形式参数和返回值的问题

形式参数:

  • 类名:需要该类的对象
  • 抽象类名:需要该类的子类对象(具体类)
  • 接口名:需要该接口的实现类对象
//类名:需要该类的对象

class Student {
    public void study() {
        System.out.println("Good Good Study,Day Day Up");
    }
}

class StudentDemo {
    public void method(Student s) { //ss; ss = new Student();  Student s = new Student();
        s.study();
    }
}

class StudentTest {
    public static void main(String[] args) {
        //需求:我要测试Student类的study()方法
        Student s = new Student();
        s.study();
        System.out.println("----------------");
        
        //需求2:我要测试StudentDemo类中的method()方法
        StudentDemo sd = new StudentDemo();
        Student ss = new Student();
        sd.method(ss);
        System.out.println("----------------");
        
        //匿名对象用法
        new StudentDemo().method(new Student());
    }
}
//抽象类名:需要该类的子类对象(具体类)

abstract class Person {
    public abstract void study();
}

class PersonDemo {
    public void method(Person p) { //p; p = new Student(); Person p = new Student(); //多态
        p.study();
    }
}

//定义一个具体的学生类
class Student extends Person {
    public void study() {
        System.out.println("Good Good Study,Day Day Up");
    }
}

//需求:我要使用PersonDemo类中的method()方法
class PersonTest {
    public static void main(String[] args) {
        PersonDemo pd = new PersonDemo();
        //目前是没有办法使用PersonDemo类的method方法
        //因为method方法的形式参数为抽象类,其没有对应的具体类
        //那么,我们就应该先定义一个具体类(Student类)
        Person p = new Student();
        //在将具体类传入method方法中
        pd.method(p);
    }
}
//接口:需要的是该接口的实现类对象

//定义一个爱好的接口
interface Love {
    public abstract void love();
}

class LoveDemo {
    public void method(Love l) { //l; l = new Teacher();  Love l = new Teacher(); 多态
        l.love();
    }
}

//定义具体类实现接口
class Teacher implements Love {
    public void love() {
        System.out.println("老师爱学生,爱Java,爱林青霞");
    }
}

class TeacherTest {
    public static void main(String[] args) {
        //需求:我要测试LoveDemo类中的love()方法
        LoveDemo ld = new LoveDemo();
        Love l = new Teacher();
        ld.method(l);
    }
}

返回值类型:

  • 类名:返回的该类的对象
  • 抽象类名:返回的是该抽象类的子类对象
  • 接口名:返回的是该接口的实现类的对象
//类名:返回的该类的对象

class Student {
    public void study() {
        System.out.println("Good Good Study,Day Day Up");
    }
}

class StudentDemo {
    public Student getStudent() {
        //Student s = new Student();
        //return s;
        return new Student();
    }
}

class StudentTest2 {
    public static void main(String[] args) {
        //需求:我要使用Student类中的study()方法
        //但是,这一次我的要求是,不要直接创建Student的对象
        //让你使用StudentDemo帮你创建对象
        StudentDemo sd = new StudentDemo();
        Student s = sd.getStudent(); //new Student(); Student s = new Student();
        s.study();
    }
}
//抽象类:返回的是该抽象类的子类对象

abstract class Person {
    public abstract void study();
}

class PersonDemo {
    public Person getPerson() {
        return new Student();
    }
}

class Student extends Person {
    public void study() {
        System.out.println("Good Good Study,Day Day Up");
    }
}

class PersonTest2 {
    public static void main(String[] args) {
        //需求:我要测试Person类中的study()方法
        PersonDemo pd = new PersonDemo();
        Person p = pd.getPerson(); //new Student();  Person p = new Student(); 多态
        p.study();
    }
}
//接口:返回的是该接口的实现类的对象

interface Love {
    public abstract void love();
}

class LoveDemo {
    public Love getLove() {
        return new Teacher();
    }
}

//定义具体类实现接口
class Teacher implements Love {
    public void love() {
        System.out.println("老师爱学生,爱Java,爱林青霞");
    }
}

class TeacherTest2 {
    public static void main(String[] args) {
        LoveDemo ld = new LoveDemo();
        Love l = ld.getLove(); //new Teacher(); Love l = new Teacher(); 多态
        l.love();
    }
}

链式编程:每次调用完毕方法后,返回的是一个对象。形式:对象.方法1().方法2().......方法n();

  • 其实在方法1()调用完毕后,应该返回一个对象;
    • 方法2()调用完毕后,应该返回一个对象。
      • 方法n()调用完毕后,可能是对象,也可以不是对象。
class Student {
    public void study() {
        System.out.println("Good Good Study,Day Day Up");
    }
}

class StudentDemo {
    public Student getStudent() {
        return new Student();
    }
}

class StudentTest3 {
    public static void main(String[] args) {
        StudentDemo sd = new StudentDemo();
        //Student s = sd.getStudent();
        //s.study();
        
        sd.getStudent().study();
    }
}

2 包

2.1 包的概述

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

包的作用:

  • A:对类进行分类管理,可以按照功能或者模块来划分
  • B:包可以**避免名字冲突。如同文件夹一样,包也采用了树形目录的存储方式
    • 同一个包中的类名字是不同的
    • 不同的包中的类的名字是可以相同的
    • 当同时调用两个不同包中相同类名的类时,应该加上包名加以区别
  • C:包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

包的定义:package 包名;,多级包用点号分开,package pkg1[.pkg2[.pkg3…]];

例如,一个Something.java 文件它的内容

package net.java.util;
public class Something{
   ...
}

那么它的路径应该是 net/java/util/Something.java 这样保存的。

注意事项:

  • A:package语句必须是程序的第一条可执行的代码
  • B:package语句在一个java文件中只能有一个
  • C:如果没有package,默认表示无包名

2.2 带包的编译和运行

  • A:手动式
    • a:编写一个带包的java文件。
    • b:通过javac命令编译该java文件。
    • c:手动创建包名。
    • d:把b步骤的class文件放到c步骤的最底层包
    • e:回到和包根目录在同一目录的地方,然后带包运行
  • B:自动式
    • a:编写一个带包的java文件。
    • b:javac编译的时候带上-d即可,格式:javac -d . HelloWorld.java
    • c:回到和包根目录在同一目录的地方,然后带包运行

带包的编译和运行(自动式)演示:

首先新建一个HelloWorld.java文件。

package com.it;

class HelloWorld {
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}

接着编译HelloWorld.java,执行 javac -d . HelloWorld.java

java会自动生成文件夹com\it,并且HelloWorld.class就在文件夹com\it下。带包运行的话执行 java com/it/HelloWorld

2.3 不同包下类之间的访问:导包

为了减少每次访问不同包下的类时要带包访问,Java就提供了一个关键字import。

格式:

  • import 包名;
  • import 包名1[.包名2…].(类名|*);

如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。

注意:package,import,class有没有顺序关系?

  • 有。package,import,class的顺序为:package > import > class,且
    • Package:只能有一个
    • import:可以有多个
    • class:可以有多个,以后建议是一个

导包示例:

编写两个文件Demo.java和Test.java。

//Demo.java
package com.crossing;

public class Demo {
    public int sum(int a,int b) {
        return a + b;
    }
}
//Test.java
package cn.cloud;

import com.crossing.Demo; //导包

class Test {
    public static void main(String[] args) {
        //访问不同包下的类时要带包访问,没有导包的情况下
        //com.crossing.Demo d = new com.crossing.Demo();
        //System.out.println(d.sum(10,20));

        Demo d = new Demo();
        System.out.println(d.sum(10,20));
    }
}

3 权限修饰符

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

3.1 公有权限修饰符-public

被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。

如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

以下函数使用了公有权限控制:

public static void main(String[] arguments) {
   // ...
}

Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。

3.2 受保护的访问权限符-protected

protected 需要从以下两个点来分析说明:

  • 子类与父类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问
  • 子类与父类不在同一包中:那么在子类中,子类实例可以访问其从父类继承而来的 protected 方法,而不能访问父类实例的protected方法

protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected

子类能访问 protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。

下面的父类使用了 protected 访问修饰符,子类重写了父类的 openSpeaker() 方法。

class AudioPlayer {
   protected boolean openSpeaker(Speaker sp) {
      // 实现细节
   }
}
 
class StreamingAudioPlayer extends AudioPlayer {
   protected boolean openSpeaker(Speaker sp) {
      // 实现细节
   }
}

如果把 openSpeaker() 方法声明为 private,那么除了 AudioPlayer 之外的类将不能访问该方法。

如果把 openSpeaker() 声明为 public,那么所有的类都能够访问该方法。

如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected。

3.3 默认权限修饰符

使用默认权限修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为public static fina,而接口里的方法默认情况下访问权限为 public

如下例所示,变量和方法的声明可以不使用任何修饰符。

String version = "1.5.1";
boolean processOrder() {
   return true;
}

3.4 私有权限修饰符-private

私有权限修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private

声明为私有权限类型的变量只能通过类中公共的 getter 方法被外部类访问。

private权限修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

下面的类使用了私有访问修饰符:

public class Logger {
   private String format;

   public String getFormat() {
      return this.format;
   }

   public void setFormat(String format) {
      this.format = format;
   }
}

实例中,Logger 类中的 format 变量为私有变量,所以其他类不能直接得到和设置该变量的值。为了使其他类能够操作该变量,定义了两个 public 方法:getFormat() (返回 format的值)和 setFormat(String)(设置 format 的值)。

3.5 访问控制和继承

请注意以下方法继承的规则:

  • 父类中声明为 public 的方法在子类中也必须为 public
  • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private
  • 父类中声明为 private 的方法,不能够被继承

3.6 类及其组成可以用的修饰符

  • 修饰符:
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final
    • 抽象修饰符:abstract
  • 类:
    • 权限修饰符:默认修饰符,public
    • 状态修饰符:final
    • 抽象修饰符:abstract
    • 用的最多的就是:public
  • 成员变量:
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final
    • 用的最多的就是:private
  • 构造方法:
    • 权限修饰符:private,默认的,protected,public
    • 用的最多的就是:public
  • 成员方法:
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final
    • 抽象修饰符:abstract
    • 用的最多的就是:public
  • 除此以外的组合规则:
    • 成员变量:public static final
    • 成员方法:public static,public abstract,public final

4 内部类

4.1 内部类概述

把类定义在另一个类的内部,该类就被称为内部类。举例:把类B定义在类A中,类B就被称为内部类。

内部类的访问特点:

  • A:可以直接访问外部类的成员,包括私有
  • B:外部类要想访问内部类成员,必须创建对象
class Outer {
    private int num = 10;
    
    class Inner {
        public void show() {
            System.out.println(num);
        }
    }
    
    public void method() {
        //找不到符号
        //show();
    
        Inner i = new Inner();
        i.show();
    }
    
}

class InnerClassDemo {
    public static void main(String[] args) { }
}

内部类的分类:

  • A:成员内部类
  • B:局部内部类
class Outer {
    private int num = 10;

    //成员位置
    //class Inner { }
    
    public void method() {
        //局部位置
        class Inner { }
    }
}

class InnerClassDemo2 {
    public static void main(String[] args) { }
}

4.2 成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:

class Circle {
    double radius = 0;
     
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

class Circle {
    private double radius = 0;
    public static int count =1;
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
            System.out.println(count);   //外部类的静态成员
        }
    }
}

不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员

  • 如果要在内部类访问外部类的同名成员,需要以下面的形式进行访问:
    • 外部类.this.成员变量
    • 外部类.this.成员方法
  • 如果要在外部类中访问成员内部类的成员:
    • 必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
    • 外部类.内部类 对象名 = 外部类对象.内部类对象;

PS:内部类和外部类没有继承关系

//访问外部类的同名成员

//需求:分别输出30,20,10。
class Outer {
    public int num = 10;
    class Inner {
        public int num = 20;
        public void show() {
            int num = 30;
            System.out.println(num);
            System.out.println(this.num);
            //方式一
            //System.out.println(new Outer().num);
            //方式二
            System.out.println(Outer.this.num);
        }
    }
}
class InnerClassTest {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }   
}
//在外部类中访问成员内部类的成员

class Circle {
    private double radius = 0;
 
    public Circle(double radius) {
        this.radius = radius;
        getDrawInstance().drawSahpe();   //必须先创建成员内部类的对象,再进行访问
    }
     
    private Draw getDrawInstance() {
        return new Draw();
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
        }
    }
}

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必须通过Outter对象来创建
         
        //第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() { }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {  //内部类
        public Inner() { }
    }
}

内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。比如上面的例子:

  • 如果成员内部类 Inner 用 private 修饰,则只能在外部类的内部访问
  • 如果成员内部类 Inner 用 public 修饰,则任何地方都能访问
  • 如果成员内部类 Inner 用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问
  • 如果成员内部类 Inner 是默认访问权限,则只能在同一个包下访问

这一点和外部类有一点不一样,外部类只能被 public 和 默认 访问两种权限修饰。由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

但是从实际开发中来说,一般内部类就是不让外接直接访问的。成员内部类的修饰符用这两种:

  • private 为了保证数据的安全性
  • static 为了方便访问数据
    • 成员内部类不是静态的访问方式是:外部类名.内部类名 对象名 = new 外部类名.new 内部类名();(非静态的成员内部类,成员只能是非静态的)
    • 成员内部类被静态修饰后的访问方式是:外部类名.内部类名 对象名 = new 外部类名.内部类名();

注意:静态内部类访问的外部类数据必须用静态修饰。

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(num);  //报错:无法从静态上下文中引用非静态 变量 num
            System.out.println(num2);
        }       
    }
}

class InnerClassDemo4 {
    public static void main(String[] args) {
        //使用内部类
        //Outer.Inner oi = new Outer().new Inner(); //报错:限定的新静态类
        //oi.show();
        //oi.show2();
        
        //成员内部类被静态修饰后的访问方式是:
        //格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
        Outer.Inner oi = new Outer.Inner();
        oi.show();
        oi.show2();
        
        //show2()的另一种调用方式,show2()是静态方法
        Outer.Inner.show2();
    }
}

4.3 局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

class People{
    public People() { }
}
 
class Man{
    public Man(){ }
     
    public People getWoman(){
        class Woman extends People{   //局部内部类
            int age =0;
        }
        return new Woman();
    }
}

注意:局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。

局部内部类:

  • A:可以直接访问外部类的成员
  • B:在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
class Outer {
    private int num  = 10;
    
    public void method() {
        //int num2 = 20;
        final int num2 = 20;
        class Inner {
            public void show() {
                //直接访问外部类的成员
                System.out.println(num);
                //从内部类中访问本地变量num2; 需要被声明为最终类型
                System.out.println(num2);//20
            }
        }
        
        //System.out.println(num2);

        //局部位置创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
        Inner i = new Inner();
        i.show();
    }
}

class InnerClassDemo5 {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

(java7)局部内部类访问局部变量必须用final修饰

  • 因为局部变量是随着方法的调用而调用,随着调用完毕而消失
  • 而堆内存的内容并不会立即消失。所以,我们加final修饰
  • 加入final修饰后,这个变量就成了常量。

(java8)局部内部类访问的局部变量不必用final修饰,这一点和Java7是不一样的。

4.4 匿名内部类

匿名内部类是局部内部类的简化形式(平时我们编写代码时用得最多的)。

前提:存在一个类或者接口(这里的类可以是具体类也可以是抽象类)。

格式:

new 类名或者接口名(){
            重写方法;
        }
interface Inter {
    public abstract void show();
    public abstract void show2();
}

class Outer {
    public void method() {
        //一个方法的时候
        /*
        new Inter() {
            public void show() {
                System.out.println("show");
            }
        }.show();
        */
        
        //二个方法的时候
        /*
        new Inter() {
            public void show() {
                System.out.println("show");
            }
            
            public void show2() {
                System.out.println("show2");
            }
        }.show();
        
        new Inter() {
            public void show() {
                System.out.println("show");
            }
            
            public void show2() {
                System.out.println("show2");
            }
        }.show2();
        */
        
        //如果我是很多个方法,就很麻烦了
        //那么,我们有没有改进的方案呢?
        Inter i = new Inter() { //多态
            public void show() {
                System.out.println("show");
            }
            
            public void show2() {
                System.out.println("show2");
            }
        };
        
        i.show();
        i.show2();
    }
}

class InnerClassDemo6 {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

本质是一个继承了该类或者实现了该接口的子类匿名对象

匿名内部类在开发中的使用:我们在开发的时候,会看到抽象类,或者接口作为参数。而这个时候,我们知道实际需要的是一个子类对象。如果该方法仅仅调用一次,我们就可以使用匿名内部类的格式简化。

interface Person {
    public abstract void study();
}

class PersonDemo {
    //接口名作为形式参数
    //其实这里需要的不是接口,而是该接口的实现类的对象
    public void method(Person p) {
        p.study();
    }
}

//实现类
class Student implements Person {
    public void study() {
        System.out.println("好好学习,天天向上");
    }
}

class InnerClassTest2 {
    public static void main(String[] args) {
        //测试
        PersonDemo pd = new PersonDemo();
        Person p = new Student();
        pd.method(p);
        System.out.println("--------------------");
        
        //匿名内部类在开发中的使用
        //匿名内部类的本质是继承类或者实现了接口的子类匿名对象
        pd.method(new Person(){
            public void study() {
                System.out.println("好好学习,天天向上");
            }
        });
    }
}

匿名内部类题:

按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
    public static void main(String[] args) {
        Outer.method().show();
    }
}
要求在控制台输出”HelloWorld”
interface Inter { 
    void show(); 
    //public abstract
}

class Outer { 
    //补齐代码
    public static Inter method() {
        //子类对象 -- 子类匿名对象
        return new Inter() {
            public void show() {
                System.out.println("HelloWorld");
            }
        };
    }
}

class OuterDemo {
    public static void main(String[] args) {
        Outer.method().show();
        /*
            1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
            2:Outer.method().show()可以看出method()方法的返回值是一个对象。
            又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
        */
    }
}

相关文章

网友评论

    本文标题:【JavaSE(五)】Java面向对象(下)

    本文链接:https://www.haomeiwen.com/subject/agnfdftx.html