JAVA学习笔记



前言

开始啃JAVA,本文不少内容都是直接Copy那本Thinking in JAVA的,参考资料如下
(PS:本文需要有C++基础)

  1. JAVA程序设计 —— 学堂在线
  2. Thinking in JAVA

基础语法 —— 题

这部分个人想了很久,最后决定以过题的方式写笔记,毕竟语法这种东西怎么说都是虚的…

一维数组与排序

字符串排序
用Java编写一个能对一组字符串按字典序升序排序的程序 输入为N和N行字符串,需要按行输出字符串升序排序的结果
如输入
3
Abc
Abe
Abd
输出:
Abc
Abd
Abe

  import java.util.*;

  public class Main{
      public static void main(String[] args){
          //创建Scanner类对象,用于输入
          Scanner cin = new Scanner(System.in);
          int n = cin.nextInt();
          String[] a = new String[n];
          for(int i = 0; i < n; i++){
              a[i] = cin.next();
          }
          //数组排序
          Arrays.sort(a);
          for(int i = 0; i < n; i++){
              System.out.println(a[i]);
          }
      }
  }


一维数组和方法

偶数分解
歌德巴赫猜想:任何一个大于六的偶数可以拆分成两个质数的和,打印出所有的可能
输入n为偶数,输出n的所有分界可能
如输入
100
输出:
100=3+97
100=11+89
100=17+83
100=29+71
100=41+59
100=47+53

  import java.util.*;

  public class Main {
      static int[] prime;
      static int[] used;
      static int ppos;

      public static void main(String[] args) {
          Euler();

          Scanner cin = new Scanner(System.in);
          int n = cin.nextInt();
          for(int i = 0; i < ppos && prime[i] <= n/2; i++){
              if(used[n - prime[i]] == 1){
                  System.out.printf("%d=%d+%d", n, prime[i], n - prime[i]);
                  System.out.println();
              }
          }
      }

      //main中直接调用的方法,声明为静态static
      //实际上是因为static方法只能访问static方法
      public static void Euler(){
          int n = (int)1e6;
          prime = new int[n];
          used = new int[n];
          //填充数组为1
          Arrays.fill(used, 1);
          ppos = 0;

          for(int i = 2; i < n; i++){
              if(used[i] == 1){
                  prime[ppos++] = i;
              }
              for(int j = 0; i*prime[j] < n; j++){
                  used[i*prime[j]] = 0;
                  if(i%prime[j] == 0)     break;
              }
          }
      }
  }


方法和递归

最大公约数和最小公倍数
输入两个正整数m和n,求其最大公约数和最小公倍数
输入
34 8
输出
2 136

  import java.util.*;

  public class Main {
      public static void main(String[] args) {
          Scanner cin = new Scanner(System.in);
          int a = cin.nextInt();
          int b = cin.nextInt();
          System.out.println(gcd(a, b) + " " + lcm(a, b));
      }

      //递归
      public static int gcd(int a, int b){
          return (b == 0 ? a : gcd(b, a%b));
      }
      public static int lcm(int a, int b){
          return a*b/gcd(a, b);
      }
  }


二维数组

扫雷
给定nxm的矩阵,其中’*‘代表雷,’.’则不是雷,求对应将’.’换成周围有几颗雷的矩阵
如输入
4 4
*…
….
.*..
….
则输出
*100
2210
1*10
1110

  import java.util.Scanner;

  public class Main {
      public static void main(String[] args){
          Scanner cin = new Scanner(System.in);
          int csn = 1;
          while(cin.hasNext()){     //相当于检测EOF
              int n = cin.nextInt();
              int m = cin.nextInt();
              if(n == 0 && m == 0)    break;

              String[] str = new String[n];
              char[][] G = new char[n][m];
              for(int i = 0; i < n; i++){
                  str[i] = cin.next();
              }
              //String不能直接修改,需转为char[]

              for(int i = 0; i < n; i++){
                  for(int j = 0; j < m; j++){
                      G[i][j] = str[i].charAt(j);   //String转char
                      if(G[i][j] == '.'){
                          G[i][j] = '0';
                      }
                  }
              }

              for(int i = 0; i < n; i++){
                  for(int j = 0; j < m; j++) {
                      if (G[i][j] != '*') continue;
                      for (int dx = -1; dx <= 1; dx++) {
                          for (int dy = -1; dy <= 1; dy++) {
                              if(dx == 0 && dy == 0)  continue;
                              int newi = i + dx;
                              int newj = j + dy;
                              if(newi < 0 || newi >= n || newj < 0 || newj >= m || G[newi][newj] == '*')  continue;
                              G[newi][newj]++;
                          }
                      }
                  }
              }

              if(csn > 1)     System.out.println();
              System.out.printf("Field #%d:", csn++);
              System.out.println();
              for(char[] G_row : G){
                  for(char ch : G_row){
                      System.out.print(ch);
                  }
                  System.out.println();
              }
          }
      }
  }


Collection

交集
给定两个数组(数组中不包含相同元素),求两个数组的交集中元素的个数(即共同出现的数,如没有则输出为None) 如输入:
5
1 2 4 6 8
6
1 2 5 6 7 8
输出: 4

  import java.util.*;

  public class Main{
      public static void main(String[] args){
          Scanner cin = new Scanner(System.in);
          int n = cin.nextInt();
          //传入Long,即long的封装类
          HashSet<Long> hset = new HashSet<>();
          for(int i = 0; i < n; i++){
              long tmp;
              tmp = cin.nextInt();
              hset.add(tmp);
          }

          int m = cin.nextInt();
          int ans = 0;
          for(int i = 0; i < m; i++){
              long tmp;
              tmp = cin.nextInt();
              if(hset.contains(tmp) == true){
                  ans++;
              }
          }
          System.out.println(ans != 0 ? ans : "None");
      }
  }


基础语法 —— 细究

一切都是对象

数据保存位置

  1. 寄存器:是根据需要由编译器分配的,我们对此没有直接的控制权
  2. 堆栈:保存某些Java数据,特别是对象句柄。创建程序时,Java编译器必须准确地知道 堆栈内保存的所有数据的“长度”以及“存在时间”,因此其灵活度不大
  3. :保存Java对象。编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此其具有很大的灵活度
  4. 静态存储:位于固定位置。可用static关键字指出一个对象的特定元素是静态的。 但Java对象本身永远都不会置入静态存储空间
  5. 常数存储:通常直接置于程序代码内部
  6. 非RAM存储:程序不运行时仍可存在,并在程序的控制范围之外

主要类型

| 主类型 | 大小 | 最小值 | 最大值 | 封装器类型 | 字段默认值 | | ——– | —— | ——— | ————– | ———- | ———- | | boolean | 1-bit | – | – | Boolean | false | | char | 16-bit | Unicode 0 | Unicode 216 - 1 | Character | ‘\u0000’ | | byte | 8-bit | -128 | +127 | Byte[11] | 0 | | short | 16-bit | -215 | +215 – 1 | Short1 | 0 | | int | 32-bit | -231 | +231 - 1 | Integer | 0 | | long | 64-bit | -263 | +263 – 1 | Long | 0 | | float | 32-bit | IEEE754 | IEEE754 | Float | 0 | | double | 64-bit | IEEE754 | IEEE754 | Double | 0L | | void | – | – | – | Void1 | - |

字面值 (Literal)

字面值用于表示固定的值 (fixed value)

类型转换

数组 (Array)


构建Java程序

一个JAVA程序

	import java.util.*;

	public class Main {
		public static void main(String[] args) {
			//Print the Date
			System.out.println(new Date());

			//Print the system info
			Properties p = System.getProperties();
			p.list(System.out);

			//Print the memory usage
			System.out.println("--- Memory Usage:");
			Runtime rt = Runtime.getRuntime();
			System.out.println("Total Memory = " + 	rt.totalMemory()
					+ " Free Memory = " + rt.freeMemory());
		}
	}


instanceof运算符

instanceof用于匹配判断对象的类型。可以用它来测试对象是否是类的一个实例,子类的实例,或者是实现了一个特定接口的类的实例

	public class Main {
		public static void main(String[] args) {
			InstanceofDemo.main();
		}
	}

	class InstanceofDemo {
		public static void main(){
			Parent obj1 = new InstanceofDemo().new Parent();
			Parent obj2 = new InstanceofDemo().new Child();
			System.out.println("obj1 instanceof Parent: "
					+ (obj1 instanceof Parent));
			System.out.println("obj1 instanceof Child: "
					+ (obj1 instanceof Child));
			System.out.println("obj1 instanceof MyInterface: "
					+ (obj1 instanceof MyInterface));
			System.out.println("obj2 instanceof Parent: "
					+ (obj2 instanceof Parent));
			System.out.println("obj2 instanceof Child: "
					+ (obj2 instanceof Child));
			System.out.println("obj2 instanceof MyInterface: "
					+ (obj2 instanceof MyInterface));

		}
		class Parent {}
		class Child extends Parent implements MyInterface {}
		interface MyInterface {}
	}

	//Output:
	//obj1 instanceof Parent: true
	//obj1 instanceof Child: false
	//obj1 instanceof MyInterface: false
	//obj2 instanceof Parent: true
	//obj2 instanceof Child: true
	//obj2 instanceof MyInterface: true


表达式、语句、块、控制流程语句


大数类

[OOP] 隐藏实施细节

包:库单元


[OOP] 类

构建

使用构建器,具体看题目

构建
编写一个表示二维平面上的点的类MyPoint,满足以下条件:
1、定义private的成员变量x和y,表示点的x和y坐标,类型为double
2、定义两个MyPoint的构造方法,一个构造方法不带参数,而且x和y的初始值为0,另一个构造方法有两个参数,参数名为x和y,类型为double,用这两个参数分别作为初始x和y坐标
3、定义一个getD方法,有一个类型为MyPoint的对象参数,功能为返回当前对象和参数对象这两个坐标点的距离,返回值为double类型 4、编写测试的main方法,调用getD计算两个点之间的距离
输入:
输入2行数据, 总共4个有理数。每2个数据一组,表示一个点的x和y坐标,每行的2个数据用空格隔开。例如:
200.1 200.2
200.3 200.4
输出:
输出两个点之间的距离。例如:
0.28284271247464315

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner cin = new Scanner(System.in);
        double x, y;
        x = cin.nextDouble();
        y = cin.nextDouble();
        MyPoint obj1 = new MyPoint(x, y);

        x = cin.nextDouble();
        y = cin.nextDouble();
        MyPoint obj2 = new MyPoint(x, y);

        System.out.println(obj2.getD(obj1));
    }
}

class MyPoint {
    private double x;
    private double y;

    public MyPoint(){
        this(0, 0);
    }
    public MyPoint(double x, double y){
        this.x = x;
        this.y = y;
    }

    public double getD(MyPoint b){
        return Math.sqrt((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y));
    }
}


this关键字

清除

参考 https://www.cnblogs.com/Smina/p/7189427.html

合成

类似C++

  class A {
      String s;
      A(){
          s = new String("Hello World!");
      }

      //编译器在希望String时会调用此方法
      public String toString(){ return s; }
  }

  public class Main {
      public static void main(String[] args) {
          A obj = new A();
          //此处编译器希望是String
          System.out.println("Test! " + obj);
      }
  }

  // Output:
  // Test! Hello World!


继承

仍然类似C++,但JAVA只允许单继承,具体见题目

继承
学校要进行年终总结,需要对教师和学生的评分结果进行统计。学生的统计数据有三个,教师的统计数据有四个。请你实现一个统计系统,对输入的数据进行整理。
请你实现一个Person类表示人员,并实现一些必要的方法,再实现Teacher类和Student类,通过类的继承机制完成这个任务。 输入格式:
首先输入一个数字N,表示输入统计的人数。
接下来是N行,每行是用空格隔开的一系列数字。
输出格式:
N行,每行是一个标识符加一个平均得分(向下取整的整数),用空格隔开。
学生的标识符是Student,教师的标识符是Teacher。
输入样例:
2
2 3 4
2 3 4 5
输出样例:
Student 3
Teacher 3

  import java.util.*;

  public class Main{
      public static void main(String[] args){
          Scanner cin = new Scanner(System.in);
          int n = cin.nextInt();
          cin.nextLine();

          for(int i = 0; i < n; i++) {
              String str = cin.nextLine();
              String[] tmp = str.split(" ");
              if(tmp.length == 3){
                  double[] score = new double[3];
                  for(int j = 0; j < 3; j++){
                      score[j] = Double.parseDouble(tmp[j]);
                  }
                  Student obj = new Student(score);
                  System.out.println(obj.getId() + " " + obj.getScore());
              }else{
                  double[] score = new double[4];
                  for(int j = 0; j < 4; j++){
                      score[j] = Double.parseDouble(tmp[j]);
                  }
                  Teacher obj = new Teacher(score);
                  System.out.println(obj.getId() + " " + obj.getScore());
              }
          }
      }
  }

  //抽象类
  abstract class Person{
      String id;
      double[] score;

      public Person(){
          this("", null);
      }
      public Person(String id, double[] score){
          this.id = id;
          this.score = score;
      }
      //抽象方法,类似C++纯虚函数
      abstract  int getScore();
      public String getId(){
          return id;
      }
  }

  final class Student extends Person{
      public Student(double[] score){
          //调用超类(基类)构建器
          super("Student", score);
      }
      public int getScore(){
          double sum = 0;
          for(int i = 0; i < 3; i++){
              sum += score[i];
          }
          return (int)Math.floor(sum/3);
      }
  }

  final class Teacher extends Person{
      public Teacher(double[] score){
          super("Teacher", score);
      }
      public int getScore(){
          double sum = 0;
          for(double num : score){
              sum += num;
          }
          return (int)Math.floor(sum/4);
      }
  }


名字隐藏

如果Java基础类有一个方法名被“过载”使用多次,在衍生类里对那个方法名的重新定义就不会隐藏任何基础类的版本。所以无论方法在这一级还是在一个基础类中定义,过载都会生效:

  class Homer {
      char doh(char c) {
          System.out.println("doh(char)");
          return 'd';
      }
      float doh(float f) {
          System.out.println("doh(float)");
          return 1.0f;
      }
  }
  class Milhouse {}
  class Bart extends Homer {
      void doh(Milhouse m) {}
  }
  class Hide {
      public static void main(String[] args) {
          Bart b = new Bart();
          b.doh(1); // doh(float) used
          b.doh('x');
          b.doh(1.0f);
          b.doh(new Milhouse());
      }
  }


final关键字

多形性(多态)

Java中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final
JAVA的多形性与C++中的virtual是类似的

  class Shape{
      void draw() { System.out.println("ERROR"); }
  }

  class Rectangle extends Shape{
      void draw() { System.out.println("Draw a rectangle"); }
  }

  class Circle extends Shape{
      void draw() { System.out.println("Draw a circle!"); }
  }

  public class Main{
      public static void main(String args[]){
          Shape[] arr = new Shape[]{ new Shape(), new Rectangle(), new Circle() };
          for(Shape obj : arr){
              obj.draw();   //多形性在此体现
          }
      }
  }

  // Output:
  // ERROR
  // Draw a rectangle
  // Draw a circle!


扩展性(接口)


抽象类和方法

相当于C++中的抽象类 具体例子见“继承”

内部类

参考

  1. https://www.cnblogs.com/dolphin0520/p/3811445.html
  2. https://www.cnblogs.com/chenssy/p/3388487.html


集合

概述

类似C++中的STL

  import java.util.Vector;

  public class Main{
      public static void main(String args[]){
          Vector<Integer> vec = new Vector();
          vec.add(1);
          vec.add(3);
          vec.add(5);
          vec.add(6);
          for(int i = 0; i < vec.size(); i++){
              System.out.println(vec.elementAt(i));
          }
      }
  }

  // Output:
  // 1
  // 3
  // 5
  // 6


迭代器

与C++类似

  import java.util.Iterator;
  import java.util.Vector;

  public class Main{
      public static void main(String args[]){
          Vector<Integer> vec = new Vector();
          vec.add(1);
          vec.add(3);
          vec.add(5);
          vec.add(6);

          Iterator<Integer> it = vec.iterator();
          while(it.hasNext()){
              System.out.println(it.next());
          }
      }
  }

  // Output:
  // 1
  // 3
  // 5
  // 6