JAVA入门学习四

时间:2022-07-28
本文章向大家介绍JAVA入门学习四,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

[TOC]

JAVA包(package)与Java API

面向对象:

  • Java包Package使用
  • 权限修饰符
  • 匿名内部类

Java API:

  • 概述了解
  • Object类介绍与使用

Java包Package

package关键字的概述及作用:

为什么要有包? 答:将字节码(.class)进行分类存放,防止重复的类名冲突

包又是什么? 答:包其实就是文件夹

包的名称写法

com.weiyigeek  #一般是域名倒着写多级包

#包的举例:
  # 学生:增加,删除,修改,查询
  # 老师:增加,删除,修改,查询

#从上面给出的案例我们可以有两种方案
#方案1:按照功能分
com.weiyi.add
  AddStudent
  AddTeacher

#方案2:按照模块分
com.hweiyigeek.teacher
  AddTeacher
  DeleteTeacher
  UpdateTeacher
  FindTeacher

包的定义格式:

package 包名;  #注意这里一般采用多级包用.分开即可
package com.weiyi.demo

定义包的注意事项

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

示例1.手动编译包中的类

#Step1.Java代码示例
package com.weiyi;  //字节码文件应该放入com/weiyi/路径中
class Demo1 {
  public static void main(String[] args)
  {
      System.out.println("Hello world! Java Package");
  }
}

#Step2.编译运行
> javac -encoding UTF-8 .Demo1.java
> mkdir com/weiyi
> copy Demo1.java com/weiyi/  #将编译生成的字节码文件复制到包路径中
> java com.weiyi.Demo1  #注意这里运行的时候需要包名+类名称
Hello world! Java Package

如何编译带包的类编译和运行?

  • a:javac编译的时候带上-d即可 javac -d . HelloWorld.java
  • b:通过java命令执行 java 包名.HellWord
import 关键字

描述:有个这个关键字就是让有包的类对调用者可见,不用写全类名了; 比如我们前面在进行Scanner类示例化的对象来接收用户输入;

导包格式 import 包名;

import java.util.Scanner
Scanner sc = new java.util.Scanner(System.in)

示例2.不同包下类之间的访问手动编译和运行

#com.weiyi 包
package com.weiyi;
public class Person {
  public String name;
  public int age;

  #构造方法
  public Person(){}
  public Person(String name,int age)
  {
    this.name = name;
    this.age = age;
  }
  
  public String getName(){
    return name;
  }
  public int getAge(){
    return age;
  }
} 

#com.demo 包
package com.demo;
public class Demo {
  public static void main(String[] args)
  {
    com.weiyi.Person p = new com.weiyi.Person("张三",23);  #注意这里是包名+类型进行调用其他包里 且类必须是公共类
    System.out.println(p.getName()+"---"+p.getAge());
  }
}

#编译步骤:
javac -encoding UTF-8 -d . Person.java  #先编译需要调用的包和类文件
javac -encoding UTF-8 -d . Demo.java  #在编译主函数类
java com.demo.Demo   #执行编译后的java文件注意任然带上包名称+类名称
张三---23

示例3.import关键字使用案例

package com.weiyigeek;
//import java.util.*;  //*通配符会到该包下挨个匹配上就导入,即该包下的所以类都是对我们可见的;
import java.util.Scanner;  //推荐方式(开发中都是导入具体类)
class Package_import {
  public static void main(String[] args)
  {
    System.out.print("[导包学习]n请输入您的名字:");
    Scanner sc = new Scanner(System.in);
    String name = sc.next();
    System.out.println("您的名字是:"+name);
  }
}

//#执行结果
[导包学习]
请输入您的名字:WeiyiGeek
您的名字是:WeiyiGeek

注意事项:

  • 面试题:package(一个java文件中只有一个),import(可以有多个导入一个包就写一个),class之间有没有顺序关系;
  • 导入是到类的名称虽然可以最后写*但是不建议这么做,当您使用到包里面的类就定义包+类名提升效率;
四种权限修饰符的测试

四种权限修饰符表格:

修饰符

本类

同一个包下(子类和无关类)

不同包下(子类就是继承)

不同包下(无关类)

private

Y

默认

Y

Y

protected

Y

Y

Y

public

Y

Y

Y

Y

类及其组成所使用的常见修饰符

  • A:修饰符:
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final
    • 抽象修饰符:abstract
  • B:类:用的最修饰符的就是 public
    • 权限修饰符:默认修饰符,public
    • 状态修饰符:final (设置后不能被子类继承)
    • 抽象修饰符:abstract
  • C:成员变量:用的最多的就是 private
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final (设置后成员变量是常量)
  • D:构造方法:用的最多的就是:public
    • 权限修饰符:private,默认的,protected,public
  • E:成员方法:用的最多的就是public
    • 权限修饰符:private,默认的,protected,public
    • 状态修饰符:static,final (设置后成员方法不能重写)
    • 抽象修饰符:abstract
  • F:除此以外的组合规则:
    • 成员变量:public static final 接口
    • 成员方法:
      • public static
        • public abstract
      • public final

基础示例:

//#com.weiyi  (外部包类)
package com.weiyi;
//#注意这里的修饰权限
public class Demo1 {
   private String love;
   public Demo1(){}
   public Demo1(String love){
     this.love = love;
   }
   public String getLove(){
     return love;
   }
   //#关键点 (protected)的修饰符
   protected void print(){
     System.out.println("这里是不能被不同包下的无关类访问的!");
   }
}

//com.weiye (集成外部包的类)
package com.weiye;
import com.weiyi.Demo1; //#注意这里需要导包使用
//#继承其他包里面类
public class Demo2 extends Demo1 {
  public Demo2(){}
  public Demo2(String love){
    //引用父类构造方法
    super(love);
  }

  public void method(){
    System.out.println("外部包继承类,访问父类中protected受保护的print方法!");
    print();
  }
}


//com.main (入口类)
package com.main;
import com.weiyi.Demo1;
import com.weiye.Demo2;
class Demo3 {
  public static void main(String[] args)
  {
    //示例1.不同包下(无关类)不能访问外部包下的除了public修饰的类和方法
    Demo1 demo1 = new Demo1("计算机");
    demo1.getLove();
    //demo1.print()  #将会报错由于不同包下的无关类不能调用外部包里面受保护的类


    //示例2.不同包下(继承子类)可以访问外部包下受保护的类protected
    Demo2 demo2 = new Demo2("运动");
    demo2.method();     //关键点
}

运行与执行结果:

[[email protected] F:Study-PromgramJAVADemosrc]$javac -encoding utf-8 -d . Demo1.java
[[email protected] F:Study-PromgramJAVADemosrc]$javac -encoding utf-8 -d . Demo2.java
[[email protected] F:Study-PromgramJAVADemosrc]$javac -encoding utf-8 -d . Demo3.java

$java com.main.Demo3
外部包继承类,访问父类中protected受保护的print方法!
这里是不能被不同包下的无关类访问的!
导包之键盘输入实例

Q:如何实现键盘录入呢? 答:键盘输入数据让程序更有灵活性,更符合开发流程可以进行调试;

  • 导包 :JAVA 底层提供的类库里面的类,就需要将此类的位置导入到JAVA文件中:import java.util.Scanner;
    • 1.语法(syntax):类名 变量名 = new 类名();
    • 创建键盘录入对象 Scanner sn = new Scanner(System.in);
    • 2.获取控制台的值,通过对象获取数据 sn.next(); 获取控制台输入的字符串类型的值 sn.nextInt(); 获取控制台输入的整形类型的值 sn.nextFloat(); 获取控制台输入的整形类型的值
    • 3.采用println()进行输出
    • 4.将sn进行关闭,释放内存 sn.close();

案例:

// 键盘输入案例
import java.util.Scanner; //导入包
class Demo_Input {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);  //#实例化Scanner类eclipse控制器输入输出类型
        System.out.print("请输入一个整数a: ");
        int x = sc.nextInt();   //将键盘输入的值存储进入到x中 (整形)
        System.out.print("请输入一个整数b: ");
        int x1 = sc.nextInt();
        System.out.print("请输入您的名字:");
        String n = sc.next(); //字符串类型
        System.out.println("名字为"+n+" ,a + b = "+(x+x1));
        sc.close();  //释放内存
    }
}
//########### 执行案例 ###############
// 请输入一个整数a: 110
// 请输入一个整数b:120
// 请输入您的名字:WeiyiGeek
// 名字为WeiyiGeek ,a + b = 230

注意事项:

  • 供外部使用的类不能采用private的修饰符,因为这样做无意义
静态导入包

概述:静态导入是JDK1.5新特性,它是导入类中的静态方法,在开发中基本不用但是还是要学习; 格式:

import static 包名….类名.方法名;  #可以直接导入到方法的级别

#基础实例
import static java.util.Arrays.sort; #静态导致

#可以直接调用
int[] arr = {11,588,687,47,10}
sort(arr);  #//直接使用静态方法名称即可

注意事项:

  • 方法必须是静态的,但是如果有多个同名的静态方法,容易不知道使用谁?
    • 这个时候要使用,必须加前缀。由此可见意义不大,所以一般不用但是要能看懂。

内部类

描述: 在内中定义类我们叫做内部类

内部类访问特点:

  • a:内部类可以直接访问外部类的成员,包括私有private。
  • b:外部类要访问内部类的成员,必须创建对象object。
  • c:实例化格式: 外部类名.内部类名 对象名 = 外部类对象.内部类对象; (注意有两个new 关键字)

成员私有内部类:

  • a:其他类无法直接访问外部类中私有成员内部类, 需要访问内部类的上级(外部)类中的公共方法进行调用;

静态成员内部类(static)

  • 成员内部类被静态修饰后的访问方式是:外部类名.内部类名 对象名 = 外部类名.内部类对象;(就不用多加一个new了)

案例演示:

class Demo_InnerClass {
  public static void main(String[] args)
  {
    //示例1.外部类要访问内部类必须是 外部类.内部类 来创建对象 (内部类极其访问特点)
    Outer.Inner demo = new Outer().new Inner(); //内部类对象 (关键点)
    demo.method();  //调用内部类方法

    //实例2.其他类无法访问外部类中私有成员内部类
    //Outer.PriInner demo1 = new Outer().new PriInner();   //会报错

    
    //实例3.可以采用Outer类中的公用方法,调用其私有内部类中的方法;
    Outer demo2 = new Outer();
    demo2.print();


    //实例4.实例化外部类中的静态成员内部类
    //外部类.内部类 对象名称 = new 外部类.内部类对象;
    Outer.StaInner demo3 = new Outer.StaInner(); //关键点
    demo3.method();
    //还有一种情况静态的内部类和内部类中的静态方法
    Outer.StaInner.print();
  }
}

//关键点:创建内部类
class Outer {
  //内部类可以看做外部类的成员
  private int number=1024;
  //(1)内部类的 公共(默认)
  class Inner {
    public void method(){
      System.out.println("内部类: 类中类!n"+"内部类可以访问外部类的私有成员number:"+number);
    }
  }

  //(2)内部类的 私有
  private class PriInner{
    public void method(){
      System.out.println("私有内部类的公共方法n"+"外部类是无法访问的,除非为本类公共方法调用!");
    }
  }
  //本类的公共方法
  public void print(){
    System.out.println("私有内部类: 外部类中的公共方法 - 调用私有的内部类里面的方法");
    PriInner i = new PriInner();
    i.method();
  }

  
  //############ 分割 #############
  //(3)静态内部类
  static class StaInner {
    public void method(){
      System.out.println("静态内部类: 以进入内部类之中");
    }
    //还有一种:静态内部类中的静态方法
    public static print(){
      System.out.println("静态内部类: -> 静态方法");
    }
  }
}

//###执行结果#####
公共内部类: 类中类!
内部类可以访问外部类的私有成员number:1024

私有内部类: 外部类中的公共方法 - 调用私有的内部类里面的方法
私有内部类的公共方法
外部类是无法访问的,除非为本类公共方法调用

静态内部类: 以进入内部类之中
静态内部类: -> 静态方法

成员内部类的面试题:

//内部类之所以能获取到外部类成员,是因为他可以获取到外部类的引用外部类名.this
class InnerClassTest {
  public static void main(String[] args) {
    Outer.Inner oi = new Outer().new Inner();
    oi.show();  //调用内部中的方法
  }	
}
//要求:使用已知的变量在控制台输出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);            //(1)就近原则方法中的变量;
      System.out.println(this.num);       //(2)类中的成员变量
      System.out.println(Outer.this.num); //(3)外部类.成员变量
    }
  }
}
局部内部类

描述: 在外部类中成员方法之中声明的类,我们叫做局部内部类;局部内部类访问局部变量必须用final修饰;

基础实例:

//#####局部内部类访问局部变量的问题#####
class Demo_localInnerClass {
    public static void main(String[] args)
    {
        Outer o = new Outer();
        o.method();
    }
}

//外部类
class Outer {
    //公共的成员方法
    public void method() {
        final int number = 1024;
        //外部类中的成员方法声明的类,我们叫做局部内部类;
        class Inner {
            public void print(){
                System.out.println("局部内部类(调用方法中的成员变量必须加上final使之成为常量):"+number);
            }
        }
        //局部内部类的调用必须在同一个成员方法中实例化
        Inner i = new Inner();//局部内部类,只能在其所在的方法中访问;
        i.print();
    }
}

//#执行结果
E:githubProjectStudy-PromgramJAVADemosrc>javac -encoding UTF-8 Demo_localInnerClass.java
E:githubProjectStudy-PromgramJAVADemosrc>java Demo_localInnerClass
局部内部类(调用方法中的成员变量必须加上final使之成为常量):1024

为什么局部内部类在访问他所在方法中的局部变量必须用final修饰?

  • 因为当调用这个方法时局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失;
  • 那么如果局部内部类对象还没有马上消失想用这个局部变量就没有了;
  • 如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在也可以继续使用;
  • 在jdk1.8中不加final也能使用,实际上没有手动添加系统底层也会默给你final;

注意事项:

  • 用画图表示:栈(stack) 堆(heap) 方法区(function area)[常量池]
    • 栈(Stack) : 方法使用进栈,结束则弹栈
    • 堆(Heap) : 实例化对象(有一个地址栈指向它)
    • 方法区 : 将类加载进入内存之中;
    • 常量池属于方法区的一部分(比如上面被final修饰的变量或者方法将存入其中);

匿名内部类

描述: 匿名内部类其实就是内部类的简化写法。 前提: 存在一个类或者interface接口(该类可以是具体类也可以是抽象类)本质是一个继承了该类或者实现了该接口的子类匿名对象。 格式:

//new 类名() 继承这个类
//new 接口名() 实现这个接口(重写方法)
new 类名或者接口名(){
  重写方法;
}

建议:匿名内部类只针对于重写一个方法时候使用,对于多个方法还是建议采用有名字的内部类;

基础实例:匿名内部类的使用

class AnonmouseInnerClass {
  public static void main(String[] args)
  {
    System.out.println("Hello World!");
    Outer demo = new Outer();  //外部类
    demo.method();  //调用匿名内部类

  }
}

//匿名类实现的接口
interface Inter {
  public void show1();
  public void show2();
}

//匿名类的外部类
class Outer {
  //建立一个公共的方法,匿名内部类重写多个方法调用
  public void method(){
    //简化版的内部类(匿名内部类)
    new Inter() {
      //重写实现接口中第一个方法
      public void show1() {
        System.out.println("匿名内部类: 重写实现接口中第一个方法");
      }
      //重写实现接口中第一个方法
      public void show2() {
        System.out.println("匿名内部类: 重写实现接口中第二个方法");
      }
    }.show1();  //方式1(关键点)实现两个方法输出需要上面这块代码重写一次;

    
    //方式2:此种方法存在弊端
    Inter i = new Inter(){
       public void show1() {
        System.out.println("匿名内部类: 重写实现接口中第一个方法");
      }
      //重写实现接口中第一个方法
      public void show2() {
        System.out.println("匿名内部类: 重写实现接口中第二个方法");
      }
      /** 弊端的产生:写一个自定义的方法
      public void show3() {
        System.out.println("匿名内部类: 自定义的方法");
      }
      **/
    };  //注意此处需要 ";"

    i.show1(); //编译看接口中定义的show1方法,运行看子类中的show1方法
    i.show2(); //编译看接口中定义的show2方法,运行看子类中的show2方法
    //i.show3(); //这里不能采用这样的形式由于编译看父类接口中定义的方法,而接口中没有这样的方法,所以会产生错误;
    //如果向下强转需要与子类的类名,而这里是匿名内部类;
  }
}

执行结果:

$javac -encoding UTF-8 AnonmouseInnerClass.java
$java AnonmouseInnerClass
Hello World!
匿名内部类: 重写实现接口中第一个方法
匿名内部类: 重写实现接口中第一个方法
匿名内部类: 重写实现接口中第二个方法

匿名内部类在开发中的应用

  • 传递参数;
  • 本质把匿名内部类看作一个对象;

基础实例:

class Demo_AnonmouseClass {
  public static void main(String[] args){
      //实例1.调用PersonDemo中的method方法
      PersonDemo pd = new PersonDemo();
      pd.method(new Student());

      //实例2.匿名内部类实现参数传递
      pd.method(new Person(){
          public void show() {
              System.out.println("匿名内部类的调用!");
          }
      });
  }
}

//抽象类(abstract)或者接口(interface)都可以;
abstract class Person {
  public abstract void show();
}
//有名称的类
class PersonDemo {
  public void method(Person p)  //Person p = new Student
  {
    p.show();
  }
}
//继承重写
class Student extends Person {
  public void show(){
    System.out.println("有名称的类");
    System.out.println("有名称的类型调用!");
  }
}

执行结果:

src> javac -encoding UTF-8 Demo_AnonmouseClass.java
src> java Demo_AnonmouseClass
有名称的类
有名称的类型调用!
匿名内部类的调用!

基础示例:匿名内部类的面试题

class Demo_AnonmouseInnerClass1 {
    public static void main(String[] args){
    //方式1:由于show方法是静态
    //链式编程每次调用方法后还能继续调用方法,证明调用方法返回的是对象;
    Outer.method().show();

    //方式2:强制转换
    Inter i = Outer.method();
    i.show();
    }
}

//匿名内部类要实现的接口
interface Inter {
    void show();
}

//外部类
class Outer {
    public static Inter method() {
        //注意这里返回的是一个匿名内部类,将重写接口方法(相当于是一个子类)
        return new Inter(){
            public void show() {
                System.out.println("匿名内部类的面试题!");
            }
        };
    }
}

执行结果:

javac -encoding utf-8 Demo_AnonmouseInnerClass1.java
java Demo_AnonmouseInnerClass1
匿名内部类的面试题!
匿名内部类的面试题!

Java API

描述: API(Application Programming Interface)应用程序编程接口,我们可以参考JDK帮助文档;

简单的说:Java API就是Java提供给我们使用的类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。

WeiyiGeek.API

帮助文档: https://blog.fondme.cn/apidoc/jdk-1.8-google/

Object类

概述: 类层次结构的根类,所有类都直接或者间接的继承自该类;

回想面向对象中为什么说? 比如我们类中的构造方法 public Object(),即子类的构造方法默认访问的是父类的无参构造方法

public class Object
Class Object是类Object结构的根。每个班都有Object作为超类,所有对象(包括数组)都实现了这个类的方法。

Object类的hashCode()方法

public int hashCode() 返回对象的哈希码值。 
#Object的hashCode()默认是返回内存地址的,但是hashCode()可以重写,所以hashCode()不能代表内存地址的不同
System.identityHashCode(Object)  #方法可以返回对象的内存地址,不管该对象的类是否重写了hashCode()方法。

支持这种方法是为了散列表,如HashMap提供的那样;返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算

Object类的getClass()方法

public final class getClass()  #返回此Object的运行时类。 
#返回的类对象是被表示类的static synchronized方法锁定的对象。

实际结果的类型是Class<? extends |X|>其中|X|是静态类型上其表达的擦除getClass被调用
#可以通过Class类中的一个方法,获取对象的真实类的全名称 public String getName()

Object类的toString()方法

public String toString()  #返回该对象的字符串表示,由于默认情况下的数据对我们来说没有意义,一般建议重写该方法。

public String toString() {
  return getClass().getName() + "@" + Integer.toHexString(hashCode());
  #左边类名 @ hashcode的十六机制整数
}

注意事项:

  • toString 方法作用可以更方便的显示属性值;
  • getAttr 方法是为了获取值,可以显示也可以赋值或其他操作;

Object类的equals()方法

public boolean equals(Object obj)  #指示其他某个对象是否与此对象“相等”。 

equals方法在非空对象引用上实现等价关系:
自反性:对于任何非空的参考值x,x.equals(x)应该返回true 。
它是对称的:对于任何非空引用值x和y, x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
传递性:对于任何非空引用值x,y和z,如果x.equals(y)回报true个y.equals(z)回报true ,然后x.equals(z)应该返回true 。
它是一致的: 对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false ,没有设置中使用的信息equals比较上的对象被修改。对于任何非空的参考值x , x.equals(null)应该返回false 。

注意事项:

  • 默认情况下比较的是对象的引用是否相同,由于比较对象的引用没有意义一般建议重写该方法。
  • ==号和equals方法的区别:
    • ==是一个比较运算符号,既可以比较基本数据类型也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值
    • equals方法是一个方法,只能比较引用数据类型,所有的对象都会继承Object类中的方法,如果没有重写Object类中的equals方法,equals方法和==号比较引用数据类型无区别,重写后的equals方法比较的是对象中的属性

基础示例:

public class Demo2 {
  public static void main(String[] args) {
    //示例1:hashCode() 的使用
    Object obj1 = new Object();
    System.out.println("Object 对象:"+obj1.hashCode());
    System.out.println("Object 对象地址:"+System.identityHashCode(obj1));
  
    //示例2.getclass() 的使用
    Person g = new Person(1,"WeiyiGeek");
    Class demo = g.getClass(); //获取对象的字节码文件
    System.out.println("Person 类名称:"+demo.getName()); //获取类名称

    //示例3.toString() 的使用
    String info = g.toString();
    System.out.println(info);
    System.out.println(g); //如果直接答应对象的引用,会默认调用toString方法
    
    
    //示例4.equals() 的使用
    //Object中的equals方法比较对象的地址值,没有什么意义,我们需要重写他;
    //因为在开发中我们通常比较对象中的属性值,我们认为相同属性是同一对象,这样我们就需要重写他;
    Person g1= new Person(1,"WeiyiGeek");
    System.out.println(g1.equals(g));  //实际还是采用下面==方式比较,但我们对方法进行了重写
    System.out.println(g1 == g );  //如果equals
  }

}

class Person {
  public String name;
  public int num = 1;
  public Person() {
    super();
  }
  public Person(int num,String name) {
    this.num = num;
    this.name = name;
  }
  //关键点1
  @Override
  public String toString() {
    System.out.println("重写toString方法:num = "+num);
    return super.toString();
  }
  //关键点2 : 重写object中的equals方法
  @Override
  public boolean equals(Object obj) {
    //向下转型
    Person s = (Person)obj;
    return this.name.equals(s.name) && this.num == s.num;
  }
}

执行结果:

#示例1:hashCode() 的使用
Object 对象:366712642
Object 对象地址:366712642

#示例2.getclass() 的使用
Person 类名称:Person

#示例3.toString() 的使用
重写toString方法:num = 1
[email protected]
重写toString方法:num = 1
[email protected]

#示例4.equals() 的使用
true
false