Java基础

Java编程基础

  • 数据转换

    1
    2
    将字符串形式的数字转换为int类型
    Integer.parseInt(strs[i]),其中的strs为字符型数组
  • String,StringBuffer和StringBuilder

    String不可变,线程安全;StringBuilder不是线程安全的,StringBuffer线程安全

  • static关键字

    • 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。静态变量通常用来声明一个类常量,即常量通常被声明为public/private,final和static类型的变量。常量初始化后不可改变。

    • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。

      非静态成员不能再静态方法中访问

    • 静态内部类

      非静态内部类依赖于外部类的实例,而静态内部类不需要。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class OuterClass {

      class InnerClass {
      }

      static class StaticInnerClass {
      }

      public static void main(String[] args) {
      // InnerClass innerClass = new InnerClass();
      // 'OuterClass.this' cannot be referenced from a static context
      OuterClass outerClass = new OuterClass();
      InnerClass innerClass = outerClass.new InnerClass();
      StaticInnerClass staticInnerClass = new StaticInnerClass();
      }
      }
      静态内部类不能访问外部类的非静态的变量和方法。
      如果想在静态方法中访问非静态变量,那么可以用:但在静态方法以及其他类中,就应该使用完全限定名ObejectReference.VariableName(实例.属性 )
      静态变量可以通过:ClassName.VariableName的方式访问。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class A {

      private static int x;
      private int y;

      public static void func1(){
      int a = x;
      // int b = y; // Non-static field 'y' cannot be referenced from a static context
      // int b = this.y; // 'A.this' cannot be referenced from a static context
      int b = new A().y; //这个是可以的
      }
      }

  • 尽量不要使用public变量

    可以使用公有的 getter 和 setter 方法来替换公有字段,这样的话就可以控制对字段的修改行为。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class AccessExample {
    public String id;
    }
    避免这样写,可以用如下方法:
    public class AccessExample {

    private int id;

    public String getId() {
    return id + "";
    }

    public void setId(String id) {
    this.id = Integer.valueOf(id);
    }
    }

  • 泛型

    允许在编译时检测非法的类型,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

    使用 场景:

    写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

    使用 Java 泛型的概念,可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

    int,double叫做数据类型,Integer,Double叫做包装类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public static < E > void printArray( E[] inputArray )
    {
    // 输出数组元素
    for ( E element : inputArray ){
    System.out.printf( "%s ", element );
    }
    System.out.println();
    }
    这里的E代表一种包装类型,包括Integer,Double,Character。但不能是基本类型比如int,double,char

    public class MaximumTest
    {
    // 比较三个值并返回最大值
    public static <T extends Comparable<T>> T maximum(T x, T y, T z)
    {
    T max = x; // 假设x是初始最大值
    if ( y.compareTo( max ) > 0 ){
    max = y; //y 更大
    }
    if ( z.compareTo( max ) > 0 ){
    max = z; // 现在 z 更大
    }
    return max; // 返回最大对象
    }
    public static void main( String args[] )
    {
    System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
    3, 4, 5, maximum( 3, 4, 5 ) );

    System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
    6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );

    System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
    "apple", "orange", maximum( "pear", "apple", "orange" ) );
    }
    }
    实现了Comparable接口的类可以直接用于比较
  • java从控制台输入字符串

    1
    2
    3
    4
    5
    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    String query=br.readLine();
    String title=br.readLine();
    System.out.println(query);
    System.out.println(title);
  • Java中的比较器

    新建最大顶堆,新建堆

    可以使用优先队列创建

    1
    2
    3
    4
    PriorityQueue<Integer> myres=new PriorityQueue<>((o1,o2)->(o2-o1))
    使用优先队列创建链表。这里为什么是大顶堆
    o1为前者,o2为后者。如果升序,则o1<o2,后面的函数需要返回-1,如何才能返回-1呢?o1-o2即可
    或者(o2-o1)<0时,传入的(o1,o2)中o1放在前面,o2放在后面

    Arrays.sort(temp,(s1,s2)->(s1+s2).compareTo(s2+s1));
    //这里是什么意思?如果s1+s2比s2+s1小,那么compareTo的返回值为-1.传入的s1放在前面,s2放在后面。否则s1放在后面,s2放在前面

    public int compareTo(Person o){

    return this.age-o.age;
    

    }
    如果升序排序,要求前者this<后者o,函数返回-1

    class SortByNumber implements Comparator{

    public int compare(Student o1,Student o2){
        return o1.getNumber()-o2.getNumber();
    }
    

    }
    int compare(Object o1, Object o2) 返回一个基本类型的整型 ,o1为前者,o2为后者
    如果要按照升序排序,则o1 小于o2,函数需要返回-1(负数)。那么如何才能返回-1呢,o1-o2即可

    1
    2

    * Java数据结构转换与使用

    利用已知的数据创建数组:new int[]{1,2,3}
    字符串转为字符数组:char[] arr=str.toCharArray();
    Java打印数组[]内容:System.out.println(Arrays.toString(arr));
    Java对数组[]排序或者Java对Collections排序:Arrays.sort(arr);Collections.sort()函数;前者对数组排序,后者可以对ArrayList排序

    java的LinkList可以用来创建栈、队列和双向队列
    int[][] samecount=new int[A.length][B.length]

    java创建队列:
    Queue queue=new LinkedList<>();用LinkedList即可创建

java创建栈

Stack res=new Stack

//先将set集合转为Integer型数组
Integer[] temp=allset.toArray(new Integer[] {});
//再将Integer型数组转为int数组
int[] intArray=new int[temp.length];
for (int i=0;i<temp.length;i++){
intArray[i]=temp[i].intValue();
}
//也可以使用toArray()方法直接转为Object对象数组,然后再逐个转为整型数组:
public static int[] SetToInt2(Set allset){
Object[] obj=allset.toArray();//先讲set集合转为Object对象数组(向上转型)
int[] temp=new int[obj.length];
for (int i=0;i<obj.length;i++){
temp[i]=(int)obj[i];//将Object对象数组转为整型数组(强制向下转型)
}
return temp;
}

1
2

* 从控制台一直扫描输入,每输入一行就执行相关运算

Scanner scan=new Scanner(System.in);
while (scan.hasNext()){
int n=scan.nextInt();
int res=getNum(n);
System.out.println(res);
}

绘图1

1
2
3
4

* Java容器包括两种为Collection与Map:前者存储对象的集合,而map存储的是键值对(两个对象)的映射表

对Collection的遍历:for (String item:list){System.out.print(item)}

java将数组转为List,java.util.Arrays#asList()方法
public static List asList(T… a)
不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
如:
int[] i ={11,22,33};
List intlist = Arrays.asList(i);
for(Object o:intlist){
System.out.println(o.toString());
}
将不能打印正确结果,因为传入的参数不能是基本类型(byte,short,int,long,float,double,boolean)数组,只能是包装类型。将int换为Integer即可

基本类型与包装类型:
基本类型:int,double,char
包装类型:Integer,Double,Character
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
Integer x = 2; // 装箱
int y = x; // 拆箱

1
2
3
4

Java中Array与ArrayList的区别:Array可以包含基本类型和对象类型,ArrayList只能包含对象类型;Array大小是固定的,ArrayList的大小是动态变化的。

数组ArrayList的序列化与反序列化:

ArrayList list = new ArrayList();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list);

ObjectInputStream ois=new ObjectInputStream(new FileInputStream(“test.txt”));
Object e=ois.readObject();
System.out.println(e);

map

1
2
3
4
5
6
7
8
9
10
11
12

TreeMap为基于红黑树的实现,HashMap为基于哈希表的实现,LinkedHashMap使用双向链表来维护元素顺序,顺序为插入顺序等。HashTable为线程安全的HashMap

问题:

说明HashSet与HashMap的底层实现?

HashMap内部存储的是一个Entry类型的数组table,即HashMap的数据结构为:transient Entry[] table;Entry存储的为键值对。内部实现时,每个Entry都是一个链表节点。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值相同的 Entry,并且拉链法插入的原则 为链表头插法。

那么如何插入一个元素:

根据元素的key,通过hash到桶的下标,即

int hash = hash(key);
int i = indexFor(hash, table.length);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

然后通过头插法插入桶对应的链表

* java枚举

**枚举限制变量只能是预先设定好的值。使用枚举可以减少代码中的 bug。**例如,我们为果汁店设计一个程序,它将限制果汁为小杯、中杯、大杯。这就意味着它不允许顾客点除了这三种尺寸外的果汁。

* 继承

一个类可以由其他类派生。如果你要创建一个类,而且已经存在一个类具有你所需要的属性或方法,那么你可以将新创建的类继承该类。

对于类A,假设类B继承自A。类A中所有的private变量和方法对于B而言都是不可见的,所以**子类拥有父类非 private 的属性、方法。**

**super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。**子类默认不继承父类的构造器或者构造方法。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。**如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。**

* 重写

子类根据自身需要重写父类中的方法。规则:参数列表必须完全与被重写方法的相同;返回类型必须完全与被重写方法的返回类型相同;访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。

* 重载

在一个类中,方法名字相同,但是参数不同的情况。我们最常见的就是构造方法的重载。

方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

* 多态

**同一个接口,适用不同的实例(对象)而执行不同的操作。**

多态存在的三个必要条件:继承、重写、父类引用指向子类对象。即对于继承了class Animal的class Dog来说,Dog实例既可以是一只狗,也可以是一只动物。即存在两个状态。

* 抽象类

注意抽象类不能用来创建实例对象,比如已经声明public abstract class Employee是抽象类,不能通过抽象类Employee来创建实例,即new Employee()会报错。抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

如果某抽象类含有抽象方法,继承该抽象类的子类一定需要实现该抽象方法。

* 接口

接口可理解为对象间相互通信的协议。接口在继承中扮演着很重要的角色。接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类。

接口不能包含成员变量,除了 static 和 final 变量。接口中可以含有变量,但是接口中的变量会被隐式的指定为 **public static final** 变量(并且只能是 public,用 private 修饰会报编译错误)。抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 **public static final** 类型的。一个类只能继承一个抽象类,而一个类却可以实现多个接口。

* java的Scanner

scan.next()与scan.nextLine()的区别:next() 不能得到带有空格的字符串;以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符,可以获得空白。

* java包package

编译之后的 .class 文件可以分享给其他编程人员,而不用透露自己的源码

java的枚举

编程时要创建常量时,可以使用Java枚举;当然除了枚举,定义private static int var也是可以的。对比上述两种方法,有以下两种区别:

使用static类静态变量定义:打印时只能知道变量的值,无法知道变量的实际含义

使用枚举定义:打印可以知道变量的实际意义,而不只是单单的数字值

下述代码演示了如何创建Java枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public enum Light {
// 利用构造函数传参
RED (1), GREEN (3), YELLOW (2);
// 定义私有变量
private int nCode ;
// 构造函数,枚举类型只能为私有
private Light( int _nCode) {
this . nCode = _nCode;
}
private int getnCode(){
return nCode;
}
// @Override
// public String toString() {
// return String.valueOf ( this . nCode );
// }
}