属性和方法

java是面向对象的编程语言

声明一个类:

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

上面创建了一个Person类,并赋予了两个属性

public表示该属性可以被外界访问

定义好了类后,需要创建实例:

Person ming = new Person();

上面就生成了一个实例

在外部可以对实例的属性进行操作

ming.age = 18;
ming.name = "xiao ming";

但是,外部操作会破坏封装性,我们可以使用private来私有化属性

class Person {
    private String name;
    private int age;
}

这样外部无法访问这两个属性 不过可以通过提供方法来修改属性

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

我们可以在定义方法时对传入的参数进行检查 避免属性被修改

为不合理的值

同样 private可以声明私有方法 这种方法只能在类的内部调用

在方法内部有一个隐含变量 this 它始终指向当前实例 比较类似

于python的self

在没有命名冲突的情况下 this可以省略

方法可以传参,在定义方法时将需要的参数严格声明

class Person{
	public void setNameAndAge(String name,int age){
		
	}
}

可变参数则用类型...这样的语法来声明,相当于数组类型 在python中的args也是同样的原理

public void setNames(String... names){
	///
}

注意 基本类型参数的传递是调用的复制的值

但引用类型的传递则会受到影响 类似于python的浅拷贝

构造方法

在实例生成时就生成好属性 相当于python的__init__

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;
    }
}

构造方法的名称就是类名 只有在使用new时才会调用构造方法 若一个类没有声明构造方法 编译器会自动生成一个空的构造方法

注意 可以有多个构造方法

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public int getAge() {
        return this.age;
    }
}

因此创建实例时 有没有参数都不会报错

没有初始化的字段 引用类型默认为NULL int为0 布尔为False

方法重载指的是声明多个名字相同的方法,传入的参数不同 以应对不同输入的场景

继承

python这一块就是瞎学的,正好复习一下

Java使用extends来实现继承

class Person {
    private String name;
    private 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字段/方法:
    private int score;

    public int getScore() { … }
    public void setScore(int score) { … }
}

注意 Person类虽然没有写extends 但事实上它是object类的继承 也就是说 只有object没有父类

一个类只能有一个父类

对于private的字段 子类无法访问

若使用protect字段修饰 则子类可以访问

super关键字表示父类 子类可以以此引用父类的属性

class Student extends Person {
    public String Hello(){
        return "Hello," + super.name;
  }
}

注意 在继承中 如果子类没有声明如何调用父类的构造方法 则编译器会自动调用父类的构造方法super();

如果参数存在问题 就会报错

instanceof用于判断某个实例是否是某个类的(子类)

多态

子类定义了一个与父类完全相同的方法 则为覆写(python中的重写)

举例

父类

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

子类

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

注意 override 必须是方法名相同 方法参数相同 方法返回值相同

加上@override可以帮助检查是否进行了正确的覆写

那么什么叫做多态呢?我们看下面这种情况

Person p = new Student();

引用类型为Person 实际类型为 Student 那么在调用方法时 生效的是Student的也就是子类的方法

这种情况就称为多态

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法

举个例子

public void runTwice(Person p) {
    p.run();
    p.run();
}

传入的参数是Person对象 在调用run方法时则根据传入对象实际的类来调用

因为所有类都继承自 object 对象 因此可以覆写object的方法toString() equals() hashCode()

在子类中 可以通过super 来调用父类没有被覆写的方法

使用final修饰的父类方法不能被覆写

同理 使用final修饰的类 不能被继承

使用final修饰的字段也不能被重新赋值

抽象类

abstract声明的是一个抽象方法

class Person {
	public abstract void run();
}

这样一个抽象方法可以没有执行语句 但可以被继承和覆写

此时 Person类将无法编译 除非声明它是一个抽象类

abstract Person {
	public abstract void run();
}

对于一个抽象类 我们无法实例化它 抽象类的价值在于被子类继承 即抽象类相当于规范

接口

使用interface可以声明一个接口

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

接口的所有方法都是默认抽象的 当一个类想要去实现一个接口时,使用implements

class Student implements Person{
		///
	}

一个类可以实现多个接口

class Student implements Person,Hello{
	///
}

接口可以拓展 使用extends

interface Hello {}
interface Person extends Hello{}

对于每个抽象方法 实现类都需要去覆写 为了避免在接口增加某个新方法后,我们要去所有的实现类里覆写该方法 可以使用

defalut来声明默认方法 该方法不需要实现类覆写

静态字段、静态方法

public static int number;

静态字段是所有实例对象共享的 即任何一个实例对它修改都相当于修改了类中的该字段

静态方法通过类名就可以调用 不需要实例对象

public static void setNumber();
Person.setNumber();

静态方法属于类而不属于实例 方法内部无法使用this变量 也不能访问实例字段

静态方法常用于工具类

每个类都有一个包名

比如 数组来自于 java.util.Arrays

package ming;
public class Person{
	///;
	}

使用import来引用其他包的class

Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:

如果是完整类名,就直接根据完整类名查找这个class;

如果是简单类名,按下面的顺序依次查找:

查找当前package是否存在这个class;

查找import的包是否包含这个class;

查找java.lang包是否包含这个class。

集合

List

Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口。Java的java.util包主要提供了以下三种类型的集合:

  • List:一种有序列表的集合,例如,按索引排列的Student的List;
  • Set:一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set;
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。

集合的接口类和实现类是分离的

List<String> list = new ArrayList<>(); // 只能放入String类型

List是有序列表 数组在增删元素时非常不方便 我们更多使用ArrayList

我们考察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()

另一种List为LinkedList 即链表 指针域指向下一个节点的位置

快速创建List

List<Integer> list = List.of(1, 2, 5);

由于List实现了迭代器接口,因此我们可以使用for each来遍历它

for (String s : list) {
            System.out.println(s);
        }

List使用 toArray()方法直接返回数组

常用写法

Integer[] array = list.toArray(new Integer[list.size()]);

List还提供了boolean contains(Object o)方法来判断List是否包含某个指定元素。此外,int indexOf(Object o)方法可以返回某个元

素的索引,如果元素不存在,就返回-1。

注意 这种方法要求比较的类是写好了equals方法的 因为查找的对象是一个新对象 如果没有写equals方法 是无法判断list里面有没有的

Map

Map类似于python的字典

HashMap
Map<String, Student> map = new HashMap<>();
map.put("Xiao Ming", s);
Student target = map.get("Xiao Ming");

Map<K, V>是一种键-值映射表,当我们调用put(K key, V value)方法时,就把key和value做了映射并放入Map。当我们调用

V get(K key)时,就可以通过key获取到对应的value。如果key不存在,则返回null。和List类似,Map也是一个接口,最常用的

实现类是HashMap

Map.keySet()获得一个可迭代对象 可以直接遍历

map.entrySet()则获得键值对集合

for (Map.Entry<String, Integer> entry : map.entrySet())

通过key计算索引的方式就是调用key对象的hashCode()方法,它返回一个int整数。HashMap正是通过这个方法直接定位key对应

的value的索引,继而直接返回value。

因此,正确使用Map必须保证:

  • 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true;
  • 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:

如果两个对象相等,则两个对象的hashCode()必须相等;

如果两个对象不相等,则两个对象的hashCode()尽量不要相等。

EnumMap

如果作为key的对象是enum类型(枚举类),那么,还可以使用Java集合库提供的一种EnumMap,它在内部以一个非常紧凑的数组存储

value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪

费。

TreeMap

TreeMap是对key排序的map,接口为SortedMap

使用TreeMap时,放入的Key必须实现Comparable接口。

如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
            public int compare(Person p1, Person p2) {
                return p1.name.compareTo(p2.name);
            }

Properties

配置文件的特点是,它的Key-Value一般都是String-String类型的,因此我们完全可以用Map<String, String>来表示它。

Set

Set和python中的集合一样 存储不重复元素的集合

  • 将元素添加进Set<E>boolean add(E e)
  • 将元素从Set<E>删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)
Set<String> set = new HashSet<>();

Queue

队列和python中的队列相同 他是先进先出的有序表

  • int size():获取队列长度;
  • boolean add(E)/boolean offer(E):添加元素到队尾;
  • E remove()/E poll():获取队首元素并从队列中删除;
  • E element()/E peek():获取队首元素但并不从队列中删除。
Queue<String> q = new LinkedList<>();

PriorityQueue

优先级队列在取出时将优先级最高的元素取出

Queue<String> q = new PriorityQueue<>();
        // 添加3个元素到队列:
        q.offer("apple");
        q.offer("pear");
        q.offer("banana");

在取出时,banana会在peer之前,因为String的compare接口按照字典序排序

因此要使用优先级队列 需要先定义好compare接口

Deque

双向队列即可以从队首和队尾同时进行的队列

Deque<String> deque = new LinkedList<>();

Deque提供的方法为

  • 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不再使用stack了 使用Deque即可实现stack

Iterator

即python中的迭代器

例如在遍历一个list对象时,实际上调用了list的迭代器对象

for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
     String s = it.next();
     System.out.println(s);
}

如果我们自己编写了一个集合类,想要使用for each循环,只需满足以下条件:

  • 集合类实现Iterable接口,该接口要求返回一个Iterator对象;
  • 用Iterator对象迭代集合内部数据。

Collections

Collections是JDK提供的工具类,同样位于java.util包中。它提供了一系列静态方法,能更方便地操作各种集合。

Collections提供了一系列方法来创建空集合:

创建空List:List<T> emptyList() 创建空Map:Map<K, V> emptyMap() 创建空Set:Set<T> emptySet()

Collections提供了一系列方法来创建一个单元素集合:

创建一个元素的List:List<T> singletonList(T o) 创建一个元素的Map:Map<K, V> singletonMap(K key, V value) 创建一个元素的Set:Set<T> singleton(T o)

collections还可以进行排序 洗牌

Collections.sort(list);
Collections.shuffle(list);