我的Java基础

IDEA快捷键

快捷键效果
mainpublic static void main(String[] args){}
soutSystem.out.println();
forifor (int i = 0; i < ; i++){}
Ctrl+Alt+L格式标准化代码
Ctrl+/自动添加单行注释
右键Generate可以生成get和set属性

访问修饰符

修饰符范围修饰对象
public所有的类可以访问类,接口,变量,方法
protected同一包内,所有子类变量,方法
private同一类内变量,方法
default同一包内类,接口,变量,方法

Java编码

ASCII

一字节,共定义了128个字符,只使用后面7位,首位统一为0

Unicode

utf-8
单个字节编码:与 ASCII相同,所以 Unicode兼容 ASCII
对于N(N>1)个字节:前N位都为1,N+1位为0,其余N-1个字节,开头均为10
utf-16
用到在学
utf-32
用到在学
备注
Unicode是可变长编码,能够对世界上所有字符都编码

内存

Java内存分为方法区

  • 堆中存放被new出来的实例对象,对象中包括:类成员变量,静态标记,方法标记,class标记。
  • 堆区被所有线程共享

  • 栈中存放正在被调用的方法与方法中的局部变量,用完即销。
  • 栈中存放类的变量的引用(堆中实例对象的地址),其实就是main方法中的局部变量。
  • 栈中的方法还要有调用者标记(知道是那个对象调用的该非静态变量),this标记。静态变量?
  • 每个线程包含一个栈区,是私有的,不能被其他线程访问。

方法区

  • 方法区只存放唯一存在的东西
  • class区用来存放每个类的class
  • 静态区存放每个类的静态成员变量和静态成员方法
  • 方法区存放每个类的方法,包括构造函数。

加载过程

  1. 加载class文件到class区域,同时加载静态成员。(初始化?)
  2. 调用main方法到栈中,为main方法中的对象的引用(局部变量)开辟内存。
  3. 在堆中为实例对象开辟内存,并为对象的成员变量进行默认初始化(有初值?),同时加载方法标记,在方法区中创建某类的方法区,并将该方法区的地址传递给方法标记。(静态标记)
  4. 利用构造函数给成员变量显示初始化。
  5. 将堆中对象的地址给到栈中的对象的引用。

数据类型加载

基本数据类型

整型大小备注
byte1个字节(8位)暂无
short2个字节(16位)暂无
int4个字节(32位)暂无
long8个字节(64位)直接赋值时要加上l或L(推荐L)
浮点型大小备注
float4个字节(32位)直接赋值时加上f或F
double8个字节(64位)暂无
字符型大小备注
char2个字节(16位)Unicode编码,单引号赋值
布尔型大小备注
boolean1位只有ture与false

引用类型

class

  1. 当一个类被创建时,引用变量会存放在中,被new出来实例对象放在中,栈中的引用变量会指向堆中的实例化对象
  2. 静态方法中,不能调用非静态方法与变量,因为静态变量与方法会首先被加载到一个内存中,而此时非静态变量与方法还没有被加载。
    类的静态变量只会加载一次,即只在内存中存一次,后续被new出来的实例,只会在内存中增加非静态变量
  3. 类中的属性当没有直接初始化,或者没有通过构造函数初始化时,编译器会默认赋值
    引用类型是null,基本类型用默认值,int0,布尔型是false

interface

暂无

enum

  1. enum类型变量只会在内存中创建一次,即无论有多少实例,地址都相同
  2. enum变量可以使用==比较,因为enum只在内存中存一次。当然最好使用equals()
public class Main16 {
  public static void main(String[] args) {
        Weekday1 d1 = Weekday1.WED;
        System.out.println(d1.dayValue);//3
        System.out.println(d1); //输出星期三
    }
}

enum Weekday1 {
    MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");

    public final int dayValue; //最好用final修饰,否则在有多个实例时,容易出现混乱
    private final String chinese;

   //用private定义构造函数,增加代码健壮性
    private Weekday1(int dayValue, String chinese) {   
        this.dayValue = dayValue;
        this.chinese = chinese;
    }

    @Override
    public String toString() {
        return this.chinese;
    }
}

构造方法

  1. 构造方法就是实例化时需要用到的方法
    Person ming = new Person();
    其中new Person()就是在调用Person类中的构造方法从而实例化一个对象
  2. 类中的构造方法可以自定义或者编译器自动添加
  3. 当用户没有自定义构造方法时,编译器会自动添加默认构造函数。
    当用户自定义构造函数时,编译器就不会添加默认构造函数
public Person(){
}
//这是默认构造函数
  1. 可以同时定义多个构造方法(方法重载?)
    编译器通过参数的参数的数量、类型、位置来确定调用哪一个构造方法
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name) {
        this.name = name;
        this.age = 12;
    }

    public Person() {
    }
}

静态字段、变量

  1. 使用static修饰
    静态字段 public static int number;
    静态方法public static void setNumber(int value) {...}
  2. 静态字段(方法)属于类,不属于实例,所以只能用类名.字段来调用。也可以使用实例化名.字段来调用,不过,编译器会自动将实例化名修改为类名
  3. 静态方法不能调用非静态字段与非静态方法,也不可以使用this,因为this属于实例对象。
    当一个类被创建时,引用变量会存放在中,实际被new出来实例对象放在中,栈中的引用变量会指向堆中的实例化对象。
    静态方法中,不能调用非静态方法与变量,因为静态变量与静态方法会首先被加载到一个内存中,而此时非静态变量与方法还没有被加载。
  4. 一个类只会创建一次静态变量与方法,而非静态变量与方法会随着实例对象的创建而多次创建。

方法重载

  1. 一个类中可以存在方法名相同,而各自的参数不同的方法,这叫方法重载
  2. 主要是用于功能类似的方法,目的是好记,容易调用
    int indexOf(int ch){}
    int indexOf(String str){}
    int indexOf(int ch, int fromIndex){}
    int indexOf(String str, int fromIndex){}
    可以是参数数量不同,可以是参数类型不同,可以是数量,类型都不同
  3. 重载的返回值可以相同,也可以不同。通常时相同的。(重载与返回值没有关系)

包装类

  • 将基本数据类型包装成引用类型的类

?: Java是面对对象编程的,但是基本数据类型不是引用类型,所有将这八种数据类型封装成引用类型,弥补基本数据类的不足。

基本类型对应的引用类型
byteByte
intInteger
shortShort
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

除了int、char是Integer、Character,其余均是同名,且首字母大写

以Integer为例
1、初始化一个Integer

int i = 100;
Integer n1 = new Integer(i);  //不推荐,虽然能用,编译器会有警告,(因为有更简单的方法?)
Integer n1 = Integer.valueOf(i);  //一般使用静态方法去创建一个实例
Integer n1 = Inter.valueOf("123");  //也可以直接使用String类型赋值
Integer n1 = i;  //Java提供的自动装箱机制,此句等同于Integer n1 = Integer.valueOf(i);

2valueOf() 源码,更好的理解上述使用静态方法创建实例
 - valueOf在类中有多个重载函数,所以可以接受整型和String型
 
 public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }
 
 3、缓存优化,为了节省内存
 当使用valueOf()创建一个实例时,若值在缓存值范围内,编译器回自动扫描缓存内是否已经存在该值,若存在则直  
 接指向该值,而不是新创一个对象。
缓存值:
 - booleantrue,false
 - byte-128127 
 - char0127 
 - short-128127 
 - int-128127 
 - long-128127 

4、不变类 (final修饰)
public final class Integer {
    private final int value;
}

5int与Integer转换

 - 正常的转换
int i = 100;
Integer n = Integer.valueOf(i);
int x = n.intValue();

-java编译器可以自动帮助我们装箱拆箱
Integer n = 100; // 编译器自动使用Integer.valueOf(int)
int x = n; // 编译器自动使用n.intValue()
常用函数功能
Integer.valueOf()实例化一个对象
Integer.parseInt()将字符串转换成Int型
Integer.toString将Int转换为字符型

继承

:当一个类想要使用另一个类的字段与方法时,可以直接继承过来,而不用再次编写。
方法:Java使用extends实现继承

class Person {
    public String name;
    public int age;

    public String getName() {...}
    public void setName(String name) {...}
    public int getAge() {...}
    public void setAge(int age) {...}
}

class Student extends Person {
    // 不需要重复name和age字段/方法,
    // 只需要定义新增score字段/方法:
    public int score;

    public int getScore() {}
    public void setScore(int score) {}
}
  • 子类继承父类所有的字段与方法,但对于private修饰的字段与方法,子类只是拥有,无法访问
  • 子类不会继承父类的构造方法,只能在子类的构造方法第一行使用super()调用。
情况一:父类构造方法无参数时
class Person{
	protected String name;
	protected int age;
	
	public Person(){}
}

class Student extends Person{
	protected int score;
	
	public Student(int score){
	super();//调用父类构造函数,可省略,编译器可以自动添加。
	this.score = score;
	}
}

情况二:父类构造方法有参数时
class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 自动调用父类的构造方法
        this.score = score;
    }
}

多态

覆写: 子类如果定义了一个与父类方法名、参数、返回值一样的函数,那么称为覆写。
备注: 任何一个不同都不能称之为覆写

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
    @Override  //可有可无,最好有,因为编译器可以用这个检查覆写是否正确
    public void run() {
        System.out.println("Student.run");
    }
}

子类覆写的访问权限不可以比父类中被覆写的访问权限严格。

向上转型

若Teacher为父类,Student为子类。
一个Teacher的引用类型,可以指向Student的实例

Teacher S = new Student() (值得思考)

Teacher是父类,Student是子类,STeacher类型的,但实例化的是StudentS具有Teacher的一切字段和方法,不拥有Student中独立于Teacher的字段与方法,但当Student覆写了Teacherd方法时,S调用的时Student内覆写的方法。

super

当子类想要调用父类的字段或方法时,可以在子类中使用super调用父类属性
super.fieldName
super.method()

final

final修饰的方法不能被覆写;
final修饰的类不能被继承;
final修饰的字段必须在创建对象时初始化,随后不可修改。

抽象

  • ?:仅仅为了子类的覆写,父类不需要有具体的实现
  • 方法:abstract修饰抽象类和抽象方法
abstract class Person {
    public abstract void run();  
}
不需要具体的代码实现,连{}都不要
  • 抽象类无法实例化
  • 子类必须覆写抽象类的抽象方法,抽象类的作用更像是为子类提供了一种规范

接口

  • :当一个抽象类没有字段,只有抽象方法,那么可以直接将抽象类改写为接口
  • 方法: interface类型,子类使用implements继承接口。
1- 接口内的所有方法都是public abstract,所以可以忽略不写
 - 接口内不可以有字段
interface Person {
    void run();
    String getName();
}

2- 子类只能继承一个父类,但子类可以继承多个接口
class Student implements Person, Hello { // 实现了两个interface
    ...
}

3- 一个接口可以使用extents去继承另一个接口
interface Hello {
    void hello();
}

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

4default
 - 接口可以定义default方法,子类没必要一定实现default方法。
 - ?:当接口想要增加新的方法时,会涉及打所有子类,使用default方法可以让部分需要的子类覆写
 - default修饰的方法也会被子类继承,子类自由决定是否覆写

public class Main22 {
    public static void main(String[] args) {
        Student6 s6 = new Student6("小李头");
        System.out.println(s6.getName()); //输出 小李头
        s6.run();//输出 小李头run
    }
}

interface Person9{
    String getName();
    default void run(){
        System.out.println(getName()+"run");
    }
}

class Student6 implements Person9{
    public String name;

    public Student6(String name){
        this.name = name;
    }

    @Override
    public String getName(){
        return name;
    }
}

抽象类与接口

抽象类接口
继承只能用extents继承一个class可以使用implements继承多个接口
字段可以定义实例字段不能定义字段
抽象方法可以定义抽象方法可以定义抽象方法
非抽象方法可以定义非抽象方法可以定义default方法

异常

定义

Java的异常分为ErrorException

  • 首先,ErrorException都是继承自Throwable
  • Error 表示十分严重的错误,程序无法解决,JVM遇到错误直接终止程序
  • Exception 表示程序运行时的错误, 可以被捕获并处理。
异常关系继承图

在这里插入图片描述

捕获异常
  • 无需捕获的异常:Error以及子类,RuntimeException以及子类。

  • 必须捕获的异常:Exception以及子类,但排除子类中的RuntimeException,这些异常也称为检查异常(Checked Exception)。

  • 异常的捕获使用 trycatchfinally组合

try{
    可能会产生异常的代码;
    
}catch (捕获的异常类型1){

    捕获该异常后所做的处理
    printStackTrace()可以打印异常栈
    
}catch (捕获异常的类型2){

	捕获该异常后所做的处理

}finally{
    可写可不写;
    用于做善后处理,例如关闭文件,资源等等;
    finally一定会执行(上述代码块包含return也会执行);
}

 - 执行完上述的异常捕获后,还会在继续执行后续的代码。(有rutern?)
 - catch语句可以多个,(只能捕获一个?)
 - 子类的异常应该放在父类的上面,防止异常覆盖

抛出异常
throw
  • 用户可以主动抛出异常。
  • 创建一个Exception实例,使用throw抛出。(抛出的是一个异常的实例、对象)
1、正常的抛出
void process2(String s) {
    if (s==null) {
        NullPointerException e = new NullPointerException();
        throw e;
    }
}

2、一般的写法
void process2(String s) {
    if (s==null) {
        throw new NullPointerException();
    }
}
  • 异常信息的改变
1、
当异常改变时,前面的异常会被后面的异常覆盖掉
public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace(); //打印异常栈
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException();
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

2、
防止上述覆盖的情况,需要将原始的Exception信息传入到新的Exception中,新的Exception就有原始Exception信息。
public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(e); //将捕获的到的原Exception信息传入到新的Exception中
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}
throws
  • 当开发者认为某函数可能会产生某种异常时,可在方法后添加throws提醒
  • 调用者调用了带有throws的方法,就必须要进行异常捕获。(仅Checked Exception)
public static int parseInt(String s, int radix) throws NumberFormatException {
    if (s == null) {
        throw new NumberFormatException("null");
    }
    ...
}
自定义异常
  • Java允许开发者自己定义异常
  • 通常先自定义一个根异常BaseException,然后在派生出各种异常子类
  • BaseException一般继承RuntimeException (?)
根异常
public class BaseException extends RuntimeException {
 根异常一般需要有多种构造方法,(构造方法的含义)
  public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

子类1
public class UserNotFoundException extends BaseException {
}
子类2
public class LoginFailedException extends BaseException {
}

反射

概述
  • 当JVM需要加载某个类时,说首先读取对应.class文件,并在内存中创建一个该类的实例Class
  • 该Class是由JVM自己创建的,开发人员无法创建,且在内存中唯一
  • 反射的目的是为了获得某个实例的信息
Class长这样
public final class Class{
	private Class(){}
}
获取Class实例(?)
获取方法代码
直接通过某个类的静态变量class获得Class cls = String.class
通过某个类的实例变量的getClass()方法获取String s = "Hello"; Class cls = s.getClass();
通过类的完整的名字域Class.getName()获得Class cls = Class.forName("java.lang.String");
访问字段
获取方法作用
Field Class对象.getField(name)根据字段名获取public的field(包括父类)
Field Classl对象.getDeclaredField(name)根据字段名获取当前类的某个field(不包括父类)
Field[] Class对象.getFields()获取所有public的field(包括父类)
Field[] Class对象.getDeclaredFields()获取当前类的所有field(不包括父类)
具体实例
import java.lang.reflect.Field;

public class Main37 {
    public static void main(String[] args) throws NoSuchFieldException {
        Class stdClass = Student10.class;
        System.out.println(stdClass.getField("score")); // public int packge1.Student10.score
        System.out.println(stdClass.getField("name")); // public java.lang.String packge1.Person12.name
        System.out.println(stdClass.getDeclaredField("grade")); //private int packge1.Student10.grade
        System.out.println(stdClass.getDeclaredField("age")); //NoSuchFieldException
        for (Field x:stdClass.getFields()){
             System.out.println(x);
         }
        //public int packge1.Student10.score public java.lang.String packge1.Person12.name
          for (Field x:stdClass.getDeclaredFields()){
              System.out.println(x);
          }
          //public int packge1.Student10.score, private int packge1.Student10.grade
    }
}

class Student10 extends Person12{
    public int score;
    private int grade;
}

class Person12{
    public String name;
    private int age;
}

获取的Field拥有一个字段的所有信息,可以通过很多方法来直接获取这些信息

  • getName():返回字段名称,例如,"name"
  • getType():返回字段类型,也是一个Class实例(?),例如,string.class
  • getModifiers():返回字段的修饰符(int类型),不同的bit表示不同的含义
具体例子:
public final class String {
    private final byte[] value;
}

Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
获取字段值
import java.lang.reflect.Field;

public class Main39 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Object p = new Person13("xiao ming");
        Class c = p.getClass();
        Field f = c.getDeclaredField("name");
        f.setAccessible(true); // 访问类中private字段值使用,表示一律访问
        Object value = f.get(p);
        System.out.println(value); //xiao ming
    }
}

class Person13{
    private String name;  //main 无妨访问类的private字段,利用f.setAccessible(true)可以访问private
//      public String name;

    public Person13(String name){
        this.name = name;
    }
}
设置字段值
import java.lang.reflect.Field;

public class Main40 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person14 p = new Person14("小明"); //小明
        System.out.println(p.getName());
        Class c = p.getClass();
        Field f = c.getDeclaredField("name");
        f.setAccessible(true);//针对非public
        f.set(p,"小红"); //因为只有一个Class,所以要指明是那个实例对象
        System.out.println(p.getName()); //小红
    }
}

class Person14{
    private String name;

    public Person14(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
获取方法
方法作用
Method getMethod(name, Class...)获取某个publicMethod(包括父类)
Method getDeclaredMethod(name, Class...)获取当前类的某个Method(不包括父类)
Method[] getMethods()获取所有publicMethod(包括父类)
Method[] getDeclaredMethods()获取当前类的所有Method(不包括父类)
import org.w3c.dom.ls.LSOutput;

public class Main41 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class c = Student11.class;

        System.out.println(c.getMethod("getScore")); //public java.lang.String packge1.Student11.getScore()

        System.out.println(c.getMethod("getNum",String.class)); //带参数String时的用法,public int packge1.Student11.getNum(java.lang.String)

        System.out.println(c.getDeclaredMethod("getGrade"));//private java.lang.String packge1.Student11.getGrade()

        System.out.println(c.getDeclaredMethod("getColor",int.class)); //参数为int时的用法,private java.lang.String packge1.Student11.getColor(int)

        System.out.println(c.getMethod("getName")); //public java.lang.String packge1.Person15.getName()

        System.out.println(c.getMethod("getStyle")); //非public 不能利用此方法获得

        System.out.println(c.getDeclaredMethod("getStyle")); //  非public,可以用此方法获得
    }
}

class Person15{
    public String name;

    public Person15(String name){
        this.name = name;
    }

    public String getName(){
        return "getName()";
    }
}

class Student11 extends Person15{
    public int age;


    public Student11(String name,int age) {
        super(name);
        this.age = age;
    }

    public String getScore(){
        return "getScore()";
    }

    private String getGrade(){
        return "getGrade()";
    }

    public int getNum(String name){
        return 1;
    }

    private String getColor(int n){
        return "getColor()";
    }

    public  String getStyle(){
        return "getStyle()";
    }
}

一个Methon包含一个方法的所有信息

  • getName():返回方法名称,例如:"getScore"
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
调用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       1、调用public方法 
        String s = "Hello,world";
        Method m = String.class.getMethod("substring", int.class); //获取方法
        Method m1 = String.class.getMethod("substring", int.class, int.class);//substring的重载函数
        String r = (String) m.invoke(s, 6); //调用invoke就是在调用该方法,后面的可变参数要与实际调用的方法一致
        String r1 = (String) m1.invoke(s, 0, 5);
        System.out.println(r); // world
        System.out.println(r1); // Hello


        2、调用静态方法   ?静态方法的修饰符
        Method m2 = Integer.class.getMethod("parseInt", String.class);
        Integer n = (Integer) m2.invoke(null, "1234");//调用静态方法,第一个填写null
        System.out.println(n); //1234

        3、获取非public函数
        Person1 p = new Person1();
        Method m3 = Person1.class.getDeclaredMethod("setName", String.class);
        m3.setAccessible(true);//对于非public函数,需要使用setAccessible来允许调用,也有可能会失败
        m3.invoke(p, "黎明");
        System.out.println(p.name); // 黎明

        Method m4 = Person1.class.getMethod("hello");
        m4.invoke(new Student1());// 多态覆写函数时,Person1的class对象,调用其子类中被覆写的函数。
                                  // 遵循多态原则,总是调用实际类的覆写方法
                                  //Student:hello
    }
}

class Person1 {
    String name;

    private void setName(String name) {
        this.name = name;
    }

    public void hello() {
        System.out.println("Person1:hello");
    }
}

class Student1 extends Person1 {
    @Override
    public void hello() {
        System.out.println("Student:hello");
    }
}

调用构造方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Main43 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor cons1 = Integer.class.getConstructor(int.class); //获取构造方法
        Integer n1 = (Integer) cons1.newInstance(123); //调用构造方法
        System.out.println(n1);
    }
}

通过Class实例获取构造方法如下

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

注意:Constructor总是当前类的构造方法,和父类无关。
调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。

获取继承关系
public class Main44 {
    public static void main(String[] args) {
        1、获得父类的Class
        Class i = Integer.class;
        Class n = i.getSuperclass(); //获得父类的Class
        System.out.println(n); //class java.lang.Number
        Class o = n.getSuperclass();
        System.out.println(o); // class java.lang.Object
        System.out.println(o.getSuperclass()); //null
        
        2、获得某个类实现的接口
        Class s = Integer.class;
        Class[] is = s.getInterfaces(); // 返回当前类直接实现的所有接口,若没有实现任何接口,则返回空数组
        for (Class i : is){
            System.out.println(i);
            // interface java.lang.Comparable
            // interface java.lang.constant.Constable
            // interface java.lang.constant.ConstantDesc
        }
    }
}
动态代理

暂时搁置

泛型

泛型就是定义一种模板
例如 ArrayList,可以看作是一个"可变长"的数组,属于Object类型
若直接用ArrayList存储StringInteger等类型,会有几个缺点

  • 需要强制转换(因为ArrayListobject,输出String类型的数据需要强制转换)
  • 不方便,易出错
import java.util.ArrayList;

public class Main50 {
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        a.add("你好,这是一个测试");
        System.out.println(a.get(0)); // 输出:你好,这是一个测试
        String s = a.get(0); // 报错java.lang.Object无法转换为java.lang.String

        ArrayList<String> s1 = new ArrayList<String>();
        s1.add("这也是一个测试");
        String s2 = s1.get(0);
        System.out.println(s2); // 输出:这也是一个测试
    }
}

所以可以使用泛型来约束ArrayList的类型

  • ArrayList<T> t = new ArrayList<T>();,将T表示任意类型
  • 例如:ArrayList<String> s = new ArrayList<String>(),这表示将ArrayList约束成String类型的
向上转型

Java标准库中的ArrayList<T>,实现了List<T>的接口,它可以向上转型为List<T>

Public class ArrayList<T> implements List<T>{
    .........................
}

List<T> array = new ArrayList<T>();

不能把ArrayList<Integer>向上转型为ArrayList<Number>List<Number>

// 创建ArrayList<Integer>类型:
ArrayList<Integer> integerList = new ArrayList<Integer>();
// 添加一个Integer:
integerList.add(new Integer(123));
// “向上转型”为ArrayList<Number>:
ArrayList<Number> numberList = integerList;
// 添加一个Float,因为Float也是Number:
numberList.add(new Float(12.34));
// 从ArrayList<Integer>获取索引为1的元素(即添加的Float):
Integer n = integerList.get(1); // ClassCastException!
使用泛型
  • 使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object
  • 当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>
  • 编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型
    例如 List<Number> list = new ArrayList<Number>();
    可以改为 List<Number> list = new ArrayList<>();
泛型接口

pass

编写泛型
  • 实际上就是提供了一种模板,在使用时,可以直接将T表示为自己想要的类型
某个具体例子:
public class Pair {
    private String first;
    private String last;
    public Pair(String first, String last) {
        this.first = first;
        this.last = last;
    }
    public String getFirst() {
        return first;
    }
    public String getLast() {
        return last;
    }
}

改写泛型:
public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}
  • 泛型<T>不能用于静态方法(???),需要单独改写"泛型"方法
public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 静态泛型方法应该使用其他类型区分,与其他泛型区分,例如<K>
    public static <K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
    }
}
  • 编写多个泛型类型
public class Pair<T, K> {
    private T first;
    private K last;
    public Pair(T first, K last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public K getLast() { ... }
}
泛型的擦拭法
  • java虚拟机对泛型其实一无所知,所有的工作都是编译器做的
1、这是我们编写的泛型代码,也是编译器看到的代码 

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

2、编译器会主动把<T>改为Object,并且根据<T>实现安全的强制转型,
   所以虚拟机看到的代码是:   
public class Pair {
    private Object first;
    private Object last;
    public Pair(Object first, Object last) {
        this.first = first;
        this.last = last;
    }
    public Object getFirst() {
        return first;
    }
    public Object getLast() {
        return last;
    }
}

3、编写使用泛型的代码,也是编译器看到的代码
Pair<String> p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();

4、虚拟机实际看到的代码,
   编译器会把所有的泛型转换成Object给虚拟机执行,在需要转型的时候,会根据实际的<T>转成响应的类型
Pair p = new Pair("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();
  • 因为java使用擦拭法,所有在实际使用时,会有局限性
局限一:<T>不能是基本类型,例如:int
        因为Object无法持有基本类型(无法强制转换等?)
Person51<int> p = new Person51<int>(1,2); // 无法通过编译

局限二:同一个泛型类的Class相等,都是Object
Person51<String> p1 = new Person51<String>("王","子恒");
Person51<Integer> p2 = new Person51<Integer>(123,456);
System.out.println(c1);  // 输出:class packge1.Person51
System.out.println(c2);  // 输出:class packge1.Person51
System.out.println(c1 == c2);  //输出:true

局限三:无法判断泛型的类型(???)
       不存在Person51<String>.class,只有唯一的Person51.class
Person51<Integer> p = new Person51<>(123,456);
Person51<String> p1 = new Person51<>("wang","ziheng");
System.out.println(p.getFirst());
System.out.println(p.getLast());

System.out.println(p instanceof Person51<String>); /* 编译报错,因为擦拭法,根本就不存在
                                                       Person51<String>这个类 */
System.out.println(p1 instanceof Person51<Integer>); //编译报错
        
System.out.println(p1 instanceof Person51<String>); //true, 为什么你可以???
System.out.println(p instanceof Person51<Integer>);  //true
System.out.println(p instanceof Person51); //true
    }

局限四:不能实例化T类型
       因为擦拭法,最终T会变成Object,这样当你想要创捷Pair<String>或者Pair<Integer>的时候,
       就会创建Object类型的实例,会被编译器阻止
class Pair<T> {
    private T first;
    private T last;
    public Pair() {
        // Compile error:
        first = new T(); //无法通过编译
        last = new T();  //无法通过编译
    }
}

若要实例化T类型,需要专门的Class<T>参数
Pair<String> pair = new Pair<>(String.class);

public class Pair<T> {
    private T first;
    private T last;
    public Pair(Class<T> clazz) {
        first = clazz.newInstance();
        last = clazz.newInstance();
    }
}
  • 不恰当的覆写方法
class Person54<T>{
    public boolean equals(T t){  //无法通过编译
        return true;
    }
}
因为编译器擦除过后,T变成Object,会覆盖Object类的方法,编译器会阻止实际上会变成覆写的泛型方法

public class Pair<T> {
    public boolean same(T t) {
        return this == t;
    }
}
换一个方法名,避免覆写
  • 泛型的继承
PASS
extends通配符
  • 当方法的形参类型时泛型时,泛型实参和泛型形参不匹配时,无法通过编译
class person55<T>{
    private T first;
    private T last;

    public person55(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getLast() {
        return last;
    }

    public void setLast(T last) {
        this.last = last;
    }
}


1、当实参泛型类型与形参泛型类型一致时,可以正常运行。
person55<Number> p = new person55<>(1,2);
	int n = add(p);
	System.out.println(n);  //输出3
	
static int add(person55<Number> p ){
	Number first = p.getFirst();
	Number last = p.getLast();
	return first.intValue() + last.intValue();
}

2、当实参泛型类型和形参泛型类型不一致,无法通过编译
person55<Integer> p = new person55<>(1,2);
	int n = add(p); //无法通过编译
	System.out.println(n);
	
static int add(person55<Number> p ){
	Number first = p.getFirst();
	Number last = p.getLast();
	return first.intValue() + last.intValue();
}
  • 使用extends通配符,解决参数不一致的问题
1、Person55<? extends Number>使得方法接收所有泛型类型为Number或Number子类的Pair类型
   所以除了可以传入Integer类型,还可以传入,double,BigDecimal等Number的子类
person55<Integer> p = new person55<>(1,2);
int n = add(p);
System.out.println(n);  //输出3

static int add(person55<? extends Number> p){
	Number first = p.getFirst();
	Number last = p.getLast();
	return first.intValue() + last.intValue();
}
  • 对于person55<T>set方法的情况
person55<Integer> p = new person55<>(1,2);
int n = add(p);
System.out.println(n);  //输出3

static int add(person55<? extends Number> p){
	Number first = p.getFirst();
	Number last = p.getLast();
	p.setFirst(new Integer(first.intValue()+100)); //无法通过编译
	p.setLast(new Integer(last.intValue()+100)); // 无法通过编译
	return p.getFirst().intValue() + p.getFirst().intValue();
}

方法参数签名setFirst(? extends Number)无法传递任何Number的子类型给setFirst(? extends Number)
原因还在于擦拭法。如果我们传入的p是Pair<Double>,显然它满足参数定义Pair<? extends Number>,然而,Pair<Double>setFirst()显然无法接受Integer类型
唯一例外 null可以,但是会抛出异常
p.setFirst(null);
p.getFirst().intValue(); java.lang.NullPointerException
  • 使用extends进行只读限制 (?? add remove 调用了set?)
int sumOfList(List<? extends Integer> list) {
    int sum = 0;
    for (int i=0; i<list.size(); i++) {
        Integer n = list.get(i);
        sum = sum + n;
    }
    return sum;
}
注意到List<? extends Integer>的限制:
 - 允许调用get()方法获取Integer的引用;
 - 不允许调用set(? extends Integer)方法并传入任何Integer的引用(null除外)。
方法参数类型List<? extends Integer>表明了该方法内部只会读取List的元素,不会修改List的元素(因为无法调用add(? extends Integer)remove(? extends Integer)这些方法。换句话说,这是一个对参数List<? extends Integer>进行只读的方法
  • 使用extends进行类型限制

public class Pair<T extends Number> { ... }
可以创建
Pair<Number> p1 = null;
Pair<Integer> p2 = new Pair<>(1, 2);
Pair<Double> p3 = null;
但不可创建非Number或其子类型
Pair<String> p1 = null; // compile error!
Pair<Object> p2 = null; // compile error!

super通配符
  • 与extends相反,super能够接受其本身和父类
class Person56<T>{
    private T first;
    private T last;

    public Person56(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getLast() {
        return last;
    }

    public void setLast(T last) {
        this.last = last;
    }
}

Person56<Number> p = new Person56<>(1.1,2.2);
Person56<Integer> p1 = new Person56<>(1,2);
setSame(p,100);
setSame(p1,100);
System.out.println(p.getFirst()+","+ p.getLast()); //输出100,100
System.out.println(p1.getFirst()+","+p1.getLast()); //输出100,100

static void setSame(Person56<? super Integer> p, Integer n){
	p.setFirst(n);
	p.setLast(n);
}
  • 对于getFirst()方法
 方法签名为 ? super  Integer getFirst()
 但无法使用Integer来接受getFirst()的值,即下列语句会报错
 Integer x = p.getFirst();
 因为,当使用p是Person<Number>的对象时,Integer无法持有Number的实例
 
因此,使用<? super Integer>通配符表示:
 - 允许调用set(? super Integer)方法传入Integer的引用;
 - 不允许调用get()方法获得Integer的引用。
 - 唯一例外是可以获取Object的引用:Object o = p.getFirst()
对比extends和super通配符
  • <? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
  • <? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)
PECS原则
  • Producer Extends Consumer Super
无限通配符
  • ? 既不能读,也不能写,只能做一些null判断
static boolean isNull(Pair<?> p) {
    return p.getFirst() == null || p.getLast() == null;
}

集合

collection

java.util包提供了集合类, collection是除了map以外所有其他集合类的根接口,java.util包主要提供了三种

  • List:一种有序列表集合
  • Set:一种保证没有重复元素的集合
  • Map:一种通过键值(key-value)查找的映射集合

Java访问集合通过统一的方式实现:迭代器(Iterator)

List

List是一种有序数组,按照顺序存放,每个元素可以通过索引确定位置,索引从0开始
List接口实现了ArrayListLinkList等实现类

List<E>接口了几个主要方法:

方法名实现功能
boolean add(E e)在末尾添加一个元素
boolean add(int index,E e)在指定索引位置添加一个元素
E remove(int index)删除指定索引的元素
boolean remove(Object e)删除某个元素
E get(int index)获取指定索引的元素
int size()获取链表大小
boolean contains(Object o)是否包含元素o
int indexOf(Object o)返回某个元素的索引

ArrayList和LinkList

功能ArrayListLinkList
内部实现数组链表
获取指定元素速度很快需要从头开始查找
添加元素到末尾速度很快速度很快
在指定位置添加/删除需要移动元素不需要移动元素
内存占用
  • 通常情况下,优先使用ArrayList

List的特点

  • List允许添加重复元素
  • List允许添加null

创建List

  • 传统的方法:List<e> list = new ArrayList<>();
  • Listof()方法:List<Integer> list = List.of(1,2,3);。(注意此方法不接受null,会报异常)

遍历List

1get(int)方法 (不推荐,就看看)
List<String> list = List.of("apple", "pear", "banana");
for (int i=0; i<list.size(); i++) {
	String s = list.get(i);
	System.out.println(s);
}

2、Iterator;Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的
   Iterator的两个方法
 - boolean hasNext();判断是否有下一个元素
 - E next();返回下一个元素
List<String> list = List.of("apple","pear","banana");
for(Iterator<String> it = list.iterator();it.hasNext();){
	String s = it.next();
	System.out.println(s);
}

3for each; for each循环本省就是iterator实现访问
List<String> list = List.of("wang","li","zhang");
for(String s:list){
	System.out.println(s);
}

ListArray的转换
pass

编写equals方法
pass

Map
  • Map是一种键值映射表
  • Map是一个接口,具体实现类主要是HashMap
  • Map的创建:Map<K,V> map = new HashMap<>();
  • Map是无序的,即不是按照存放顺序存在Map中的
方法功能
put(K key,V value)keyvalue关联,并放在Map
V get(K key)获得指定key对应的value,若key不存在,则返回value
boolean containsKey(K key)查询某个Key是否存在
  • Map中不可以存在两个相同的key,即新存入的(key,value)会顶掉旧的(key,value)
  • Map中可以存在相同的value

遍历Map

1keySet()方法,包含map中所有key集合
Map<String,Integer> map = new HashMap<>();
map.put("apple",43);
map.put("pear",7878);
map.put("banana",9090);
for(String key:map.keySet()) {
    Integer value = map.get(key);
    System.out.println(key + ":" + value); //输出 banana:9090  apple:43  pear:7878
}

2entrySet()方法,包含map中的(key,value)
Map<String,Integer> map = new HashMap<>();
map.put("apple",43);
map.put("pear",7878);
map.put("banana",9090);

for(Map.Entry<String,Integer> entry : map.entrySet()){
	String s = entry.getKey();
	Integer value = entry.getValue();
	System.out.println(s+" "+value); //输出banana 9090  apple 43 pear 7878
}
编写equals和hashcode

pass

使用EnumMap
  • key的对象是enum类型,可以使用EnumMapEnumMap内部使用的是紧凑的数组存储value,不由计算hashCode(),直接定位到内部数组的索引。
  • 当key的对象是enum类型的时候,尽量使用EnumMap,不但效率最高,而且没有额外的空间的浪费
import java.time.DayOfWeek;
import java.util.EnumMap;
import java.util.Map;

public class Main59 {
    public static void main(String[] args) {
        Map<DayOfWeek,String> map = new EnumMap<DayOfWeek, String>(DayOfWeek.class); //???
        map.put(DayOfWeek.MONDAY,"星期一");
        map.put(DayOfWeek.TUESDAY,"星期二");
        map.put(DayOfWeek.WEDNESDAY,"星期三");
        map.put(DayOfWeek.THURSDAY,"星期四");
        map.put(DayOfWeek.FRIDAY,"星期五");
        map.put(DayOfWeek.SATURDAY,"星期六");
        map.put(DayOfWeek.SUNDAY,"星期天");
        System.out.println(map);
        System.out.println(map.get(DayOfWeek.MONDAY));
    }
}
TreeMap
  • TreeMapSortedMap的实现类,SortedMap是一个接口,继承Map接口
  • SortedMap保证遍历时,以key的顺序来进行排序
import java.util.Map;
import java.util.TreeMap;

public class Main60 {
    public static void main(String[] args) {
        Map<String,Integer> map = new TreeMap<>();
        map.put("apple",1);
        map.put("pear",2);
        map.put("banana",3);
        for (String key: map.keySet()) {
            System.out.println(key);  //apple  banana  pear
        }
    }
}
  • 内部Comparable的实现

Pass

使用Properties

读取配置文件

  • Properties用于读写配置文件,我们只需要只要使用Properties读写配置的接口
  • 配置文件的接口的特点:Key-Value一般都是String-String类型的

Properties读取配置文件一共有三步:

  • 创建Properties实例
  • 调用load()读取文件
  • 调用getProperty()获取配置文件
1、从文件中读取配置文件

 - 这是一个典型的配置文件:'#' 开头的是注释
# setting.properties
last_open_file=/data/hello.txt
auto_save_interval=60

 - 从系统文件中读取这个
String f = "setting.properties" //这里表示文件的路径
Properties props = new Properties();
props.load(new java.io.FileInputStream(f)); //load()方法接受一个InputStream实例,它是一个字节流

String filepath = props.getProperty("last_open_file"); //获取键值,当key不存在,返回null
String interval = props.getProperty("auto_save_interval","120") //当key不存在,则返回120

2、从内存读取一个字节流
 String settings = "# test" + "\n" + "course = Java" + "\n" + "last_open_date = 2021-04-18-11:25";
ByteArrayInputStream  input = new ByteArrayInputStream(settings.getBytes("UTF-8"));
Properties props = new Properties();
props.load(input);

System.out.println(props.getProperty("course"));  //Java
System.out.println(props.getProperty("last_open_date")); //2021-04-18-11:25
System.out.println(props.getProperty("last_open_file")); //null
System.out.println(props.getProperty("auto_save","不存在")); //不存在

写入配置文件

  • 通过setProperty()修改了Properties实例,可以把配置写入文件,写入配置使用store()方法
public static void main(String[] args) throws IOException {
        Properties props = new Properties();
        props.setProperty("url","123");
        props.setProperty("learn","Java");
        props.store(new FileOutputStream("D:\\test.properties"),"12121212");
}
  • 结果如下所示

什么描述

在这里插入图片描述

编码

  • load(InputStream)默认用ASCII编码读取字节流,所以会导致读到乱码。
  • 需要另一个重载方法load(Reader)读取
Properties props = new Properties();
props.load(new FileReader("settings.properties", StandardCharsets.UTF_8));
使用set

Set用于存储不重复的元素集合,有如下方法:

调用名功能
boolean add(E e)将元素e添加至Set<E>
boolean remove(Object e)删除元素e
boolean contains(Object e)判断是否包含元素e

例:

Set<String> set = new HashSet<>();
System.out.println(set.add("abc"));//输出true
System.out.println(set.add("xyz"));//输出true
System.out.println(set.add("abc"));//输出false ,因为set中已经存在了abc
System.out.println(set.contains("abc"));//输出true
System.out.println(set.contains("xyz"));//输出ture
System.out.println(set.contains("opq"));//输出false,因为set中不存在opq

最长用的Set的实现类是HashSet
Set接口并不保证有序,而SortedSet接口则保证元素是有序的:

  • HashSet是无序的,因为它实现了Set接口,并没有实现SortedSet接口;
  • TreeSet是有序的,因为它实现了SortedSet接口。
    在这里插入图片描述
    例:
1、HashSet
Set<String> set = new HashSet<>();
set.add("apple");
set.add("pear");
set.add("banane");
set.add("orange");
System.out.println(set); //[orange, apple, pear, banane]

2、TreeSet
Set<String> set = new TreeSet<>();
set.add("apple");
set.add("pear");
set.add("banane");
set.add("orange");
System.out.println(set); //[apple, banane, orange, pear]
  • TreeMap一样,使用TreeSet要实现Comparable接口
使用Queue
  • 队列,先进先出

队列接口定义了几个方法:

  • int size():获取队列的长度
  • boolean add(E)/boolean offer(E):添加元素到队尾
  • E remove()/E poll():获取队首元素并从队列中删除
  • E element()/E peek():获取队首元素但并不从队列中删除。

对于同一种功能有两个方法,在调用失败时,行为时不同的

抛出异常返回false或null
添加元素到队尾add(E e)boolean offer(E e)
取队首元素并删除E remove()E poll()
取队首元素但不删除E element()E peek()
使用PriorityQueue
  • PriorityQueueQueue的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue调用remove()poll()方法,返回的总是优先级最高的元素

例:

Queue<String> q = new PriorityQueue<>();
q.offer("apple");
q.add("pear");
q.offer("banana");
System.out.println(q); // [apple, pear, banana]  猜想:还是按照队列的方式存储,只不过在取出的时候,利                        
                       // 用比较去除优先级最高的
System.out.println(q.poll()); //apple
System.out.println(q.poll()); //banana
System.out.println(q.poll()); //pear
System.out.println(q.poll()); //null
  • 放入PriorityQueue的元素,必须实现Comparable接口,用于比较优先级

pass

使用Deque
  • 双端队列,允许两头都进,两头都出
  • Deque是一个接口,并且扩展自Queue

方法如下:

调用名功能
addLast(E e) /offerLast(E e)添加元素到队尾
E removeFirst() / E pollFirst()取队首元素到并删除
E getFirst() / E peekFirst()取队首元素但不删除
addFirst(E e) / offerFirst(E e)添加元素到队首
E removeLast() /E pollLast()取队尾元素并删除
E getLast() / E peekLast()取队尾元素但不删除
Stack

Java中使用Deque作为Stack使用
调用方法如下:

  • push(E):把元素压入栈
  • pop():把栈顶的元素弹出
  • peek():取栈顶元素,但不弹出
Collections
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值