相关文章
Java学习笔记 02 | 快速之旅:Java环境配置及HelloWorld程序
引言
写这篇文章,主要是为了以后能快速复习Java的基础语法;同时,帮助有C++等语言基础的同学快速入门Java
目录
Java是一门面向对象的语言,面向对象有三大特性:封装、继承、多态
面向对象编程(Object Oriented Programming,OOP)的一个核心问题在于把一类具有共同特征的事物抽象为类
- //定义一个类A
- public class A {
-
- //静态成员变量:
- public static int num;
- //实例成员变量:
- public String id;
-
- //静态方法(类方法):
- public static void staticFunc(){
-
- }
- //实例方法:
- public void func(){
-
- }
- //构造方法:
- public A(){
- this.id = "0";
- }
-
- }
使用 new 关键字创建类的对象
- //类的实例化 —— 对象a
- A a = new A();
-
- //调用实例方法
- a.func();
-
- //调用类方法,类方法通过类名可以直接调用,无需实例化
- A.staticFunc();
使用 extends 继承父类
- //父类A
- class A {
- public void doNothing(){}
- }
- //A的子类B
-
- class B extends A {
-
- }
注意
- 一个*.java源文件中可以包含多个类,比如A.java可以包含类A和类B。在上面的例子中,类A和类B访问控制级别为缺省的 default
- 但公有类(带 public 修饰的类)要求源文件名必须和公有类名相同,所以一个*.java源文件最多只能包含一个带public修饰的类
- 当一个*.java源文件包含多个类时,编译后会生成多个*.class文件,每个类对应一个class文件
对于Java而言, 一切皆对象。Java不存在全局函数和全局变量
一个最简单的Hello Worl程序,在Java中是这样的:
- public class Hello {
- public static void main(String[] args) {
- System.out.println("Hello world!");
- }
- }
随着程序的开发,我们编写的类越来越多,难免出现类重名的情况,为了解决这个问题,我们把具有类似功能的类放到一个包中。Java允许类重名,只要它们处于不同的包
包(package)是组织类的一种方式。简单理解,包就相当于文件夹,而类对应着文件,一个文件夹(包)中可以存放多个文件(类)或文件夹(包)
包的创建使用 package 包名 的形式
在一个工程中,包名应当唯一,习惯上包名采用域名的颠倒形式
package pers.myproject.myclass;
如果项目中没有使用package创建包,系统会自动生成一个默认的包来存放编写的类
包的导入使用 import 包名 的形式,比如:
- import java.util.ArrayList;
- import java.util.List;
import包的目的在于使用包中的类时,不必像下面这样输入完整的路径
java.util.List list = new java.util.ArrayList();
包 | 功能 |
java.lang | Java语言包,包含Object、String等基础类,对象,基本数据类型的包装类。自动导入,无需import |
java.util | 工具类包,含有集合类、Date类、Scanner类等 |
java.io | 主要含有与输入/输出相关的类 |
java.net | 网络编程开发包 |
java.sql | 进行数据库开发的支持包 |
java.awt | 提供创建图形用户界面相关的类 |
java.security | 提供安全方面的支持 |
Java的注释有三种
//这是一段行注释
- /*
- 这是
- 多行注释
- */
文档注释是指在程序中采用特定的格式进行注释,然后通过JDK提供的javadoc工具解析,生成一套网页形式的程序说明文档
- /**
- * @author 我
- * @version 1.0
- */
- public class Main {
- public static void main(String[] args) {
- System.out.println(add(1,2));
- }
-
- /**
- * 加法
- * @param a 被加数
- * @param b 加数
- * @return 整型
- */
- public static int add(int a, int b){
- return a+b;
- }
- }
1)打开*.java源文件所在目录,复制源文件所在文件夹的路径
2)[Windows+R]>输入cmd>输入 cd [*.java所在文件夹的路径]
3)输入 javadoc -d [存储文档注释的路径] -encoding UTF-8 -charset UTF-8 -author -version [*.java]
- cd C:\AJava\untitled1\src
- javadoc -d C:\AJava\DocComment -encoding UTF-8 -charset UTF-8 -author -version Main.java
4)找到存储文档注释的路径,双击index.html
5)文档注释的效果如下:
访问控制 | private、default、protect、public |
类、方法和变量修饰符 | abstract、class、new、enum、extends、final、implement、interface、native、static、strictfp、synchronized、transient、volatile |
程序控制 | for、break、continue、do、while、if、else、switch、case、instanceof、return |
异常处理 | try、catch、finally、throw、throws、assert |
基本类型 | byte、short、int、long、float、double、boolean、char |
变量引用 | super、this、void |
包相关 | import、package |
保留字 | goto、const |
注意:
1.根据官方文档的定义,看起来像关键字的null、true、false不是关键字,它们是字面量
2.访问控制的级别(√ 表示可以访问):
作用域
同一个类
同一个包
子类(可能不在同一个包)
不同包
private
√
default
√
√
protect
√
√
√
public
√
√
√
√
Java一切皆对象,对于基础数据类型,Java也实现了它们对应的包装类
基本数据类型 | 名称 | 长度(字节) | 对应包装类 |
byte | 字节型 | 1 | Byte |
short | 短整型 | 2 | Short |
int | 整型 | 4 | Integer |
long | 长整型 | 8 | Long |
float | 单精度浮点型 | 4 | Float |
double | 双精度浮点型 | 8 | Double |
boolean | 布尔型 | 1 | Boolean |
char | 字符型 | 2 | Character |
注意:
1.Java没有无符号数
2.Java中的整数默认为int类型,浮点数默认为double类型
因此下面两种写法是非法的:
long val = 10000000000;//整数10000000000默认为int类型,不能直接赋值给long类型变量
float f = 3.14;//浮点数3.14默认为double类型,不能直接赋值给float类型变量
应当写成下面的形式:
long val1 = 10000000000L; long val2 = 10000000000l; float f1 = 3.14F; float f2= 3.14f; float f3 = (float)3.14;
Java没有全局变量,因此Java中的变量要么是类的成员(成员变量),要么在类的方法或语句块中。成员变量可以进一步分为静态成员变量和实例成员变量
静态变量需要使用 static 关键字修饰,在类加载期间都可以通过类访问其静态变量,不必创建实例就可以访问
实例变量随着对象的创建而存在,随着对象的删除而销毁,实例变量必须通过实例对象才能访问
- public class Main {
-
- int v1; //实例成员变量
- static double v2; //静态成员变量
-
- public static void main(String[] args) {
-
- int v3 = 0; //局部变量
-
- //变量的访问:
- Main m = new Main();
- System.out.println(m.v1);
- System.out.println(Main.v2);//通过类名访问其静态成员变量
- System.out.println(v2); //在同一个类中,可以直接访问其静态成员变量
- System.out.println(v3);
- }
- }
Java的常量使用 final 关键字定义
final int NO = 123456;//习惯上我们会大写常量的标识符
和C++类似,Java的常量也具有类型,比如上例中常量NO的类型为int;和C++不同的是,C++的常量使用const修饰符定义,而Java使用final关键字
+ - * / % ++ -- 等
> < == != >= <= 等
- //格式:a instanceof ClassA
- //判断对象a是否是类ClassA的实例,如果是则返回true
-
- //实例代码:
- public class Main {
- public static void main(String[] args) {
- Main m = new Main();
- System.out.println(m instanceof Main);
- }
- }
&& || ! & | ^
= += -= *= /= 等
- >>(右移) <<(左移) &(按位与) |(按位或) ^(异或) ~(反码)等
-
- 3 << 1 == 6 //3左移1位
- 3 << 2 == 12 //3左移2位
- if(布尔表达式){
- ...
- }else if(布尔表达式){
- ...
- }else{
-
- }
- int type = 0;
- switch (type) {
- case 0:
- //语句1
- break;
- case 1:
- //语句2
- break;
- default:
- break;
- }
- for(int i = 0; i < 100; i++){
- ...
- }
- int[] arr = {1,2,3,4};
- for(int vi : arr){
- System.out.println(i);
- }
- //当型循环,满足条件时才执行循环体
- while(布尔表达式){
- 循环体
- }
- //直到型循环,循环体至少执行一次
- do{
- 循环体
- }while(布尔表达式)
String str = "hello";
- String str1 = "hello";
- String str2 = "world";
- String str = str1 + str2; //str = "helloworld"
- String str = "hello";
-
- //输出字符串str的长度:
- System.out.println(str.length());//输出 5
-
- //输出str中索引为0的字符:
- System.out.println(str.charAt(0));//输出 h
-
- //输出字符'l'在字符串中第一次出现时的位置索引:
- System.out.println(str.indexOf('l'));//输出 2
-
- //比较两个字符串的内容是否相等:
- String str1 = "abc";
- String str2 = "abc";
- if(str1.equals(str2)){
- System.out.println("str1 == str2");
- }
- public class Main {
- public static void main(String[] args) {
- String str = "hello";
- for(int i = 0; i < str.length(); i++){
- System.out.println(str.charAt(i));
- }
- }
- }
- int[] arr1 = {1,2,3,4,5};
- int[] arr2 = new int[]{1,2,3,4,5};
- int[] arr3 = new int[5];
- for(int i = 0; i < 5; i++){
- arr3[i] = i + 1;
- }
定义数组时,数组的长度可以由变量指定
- int n = 5;
- int[] arr = new int[n];
- int[][] arr1 = {{1,2,3},{4,5,6}};
- int[][] arr2 = new int[2][3];
- int no = 1;
- for(int i = 0; i < 2; i++){
- for(int j = 0; j < 3; j++){
- arr2[i][j] = no;
- no++;
- }
- }
动态数组有多种实现方法,比较常见的是用ArrayList实现动态数组。ArrayList是List的一个实现类,它的底层是通过数组实现的
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- public class Main {
- public static void main(String[] args) {
- //创建一个动态数组:
- List
arraylist = new ArrayList(); - //List arraylist = new ArrayList();
-
- for(int i = 1; i < 6; i++){
- arraylist.add(i); //在数组尾插入元素i
- }
- arraylist.add(6); //在数组尾插入元素6
-
- System.out.println(arraylist.size()); //输出数组的长度
- //遍历ArrayList数组方法一:
- for(int i = 0; i < arraylist.size(); i++){
- System.out.print(arraylist.get(i)); //get(i)获取下标为i的元素
- }
- System.out.println();
-
- //删除索引(下标)为5的元素:
- arraylist.remove(5);
-
- //遍历ArrayList数组方法二:
- for (Integer i : arraylist) {
- System.out.print(i);
- }
- System.out.println();
-
- //遍历ArrayList数组方法三:
- Iterator
it = arraylist.iterator(); - while (it.hasNext()){
- Integer i = it.next();
- System.out.print(i);
- }
- }
- }
注意
在语句 List
arraylist = new ArrayList 中,();
是泛型,List集合的元素应当是对象,而不能是基础数据类型,所以不能用 而要用。Integer是int的封装类型,在Java的自动装箱机制下,可以在需要的时候自动将int类型转化成Integer对象
Java集合框架(Java Collections Framework,JCF)是为表示和操作集合而规定的一种统一的标准的体系结构
集合的特点:集合的长度可变;集合只能存储引用类型数据,但存储的对象可以是不同数据类型
Java集合类的定义在包 java.util 下,有两个根接口 Collection 和 Map。Collection是单列集合,一次只存储一个元素;Map是双列集合,一次存储一个键值对
注意
集合存储的元素必须是对象,不能是基本数据类型,想要存储基本类型数据可以使用它们对应的包装类(Byte、Short、Integer、Long、Float、Double、Boolean、Character)
Java中栈的实现方法有很多,这里采用Deque(双端队列)来实现栈
- import java.util.ArrayDeque;
- import java.util.Deque;
-
- public class Main {
- public static void main(String[] args) {
- //定义一个栈:
- Deque
deque = new ArrayDeque(); - //入栈:
- deque.addLast('a');
- deque.addLast('b');
- deque.addLast('c');
- deque.addLast('d');
- //出栈:
- char ch = deque.removeLast(); //removeLast()移除deque的最后一个元素并将其返回
- //栈的遍历:
- while (!deque.isEmpty()){
- char c = deque.removeLast();
- System.out.println(c);
- }
- }
- }
输出:
队列也可以采用Deque(双端队列)来实现
- import java.util.ArrayDeque;
- import java.util.Deque;
-
- public class Main {
- public static void main(String[] args) {
- //定义一个队列:
- Deque
deque = new ArrayDeque(); - //入队:
- deque.addLast('a');
- deque.addLast('b');
- deque.addLast('c');
- deque.addLast('d');
- //出队:
- char ch = deque.removeFirst(); //removeFirst()移除deque的最后一个元素并将其返回
- //遍历整个队列:
- while (!deque.isEmpty()){
- char c = deque.removeFirst();
- System.out.println(c);
- }
- }
- }
输出:
- import java.util.*;
-
- public class Main {
- public static void main(String[] args) {
- //定义一个哈希表:
- Map
map = new HashMap(); -
- //添加键值对:
- map.put('a',"apple");
- map.put('b',"banana");
- map.put('c',"coconut");
-
- //根据键值获取value:
- String strc = map.get('c');
-
- //map.containsValue("coconut")判断map是否包含Value为"coconut"的元素
- //map.map.containsKey('c')判断map是否包含Key为'c'的元素
- if(map.containsValue("coconut")){
- map.remove('c');//根据键值删除元素:
- }
-
- System.out.println(map.size()); //输出集合包含的元素个数
- //遍历Map的方法一:
- for(Character ch : map.keySet()){
- //遍历keySet集合,再根据key找value
- System.out.println(ch + ":" + map.get(ch));
- }
- System.out.println();
-
- //遍历Map的方法二:
- for (Map.Entry
entry : map.entrySet()){ - //通过entrySet()方法将map转换成Entry集合,遍历Entry集合的元素entry
- System.out.println(entry.getKey() + ":" + entry.getValue());
- }
- System.out.println();
-
- //遍历Map的方法三:
- Iterator
> iterator = map.entrySet().iterator(); - while (iterator.hasNext()){
- //使用迭代器遍历map
- Map.Entry
next = iterator.next(); - System.out.println(next.getKey()+" : "+next.getValue());
- }
- }
- }
- public class Main {
- public static void main(String[] args) {
- String str1 = "Hello";
- String str2 = " world!";
- int n = 123456;
- System.out.println(str1);
- System.out.println(str1 + str2 + n);
- System.out.print(str1 + str2 + n);
- System.out.printf("字符串:%s%s,数字:%d",str1,str2,n);
- }
- }
比较常见的方法是采用Scanner输入
- import java.util.Scanner;
-
- public class Main {
- public static void main(String[] args){
- Scanner scanner = new Scanner(System.in);
- //读取整数:
- int a = scanner.nextInt();
- int b = scanner.nextInt();
- int c = scanner.nextInt();
- //读取一个字符串(不含空格):
- String s = scanner.next();
- //读取一行文字:
- String text = scanner.nextLine();
-
- //输出:
- System.out.println(a + " " + b + " " + c);
- System.out.println(s);
- System.out.println(text);
- }
- }
输出:
Object 是 java.lang 包下的一个核心类,是所有类的超类,即Object是所有类的祖先。我们定义一个类时,编译器会默认在类名之后加 extend Object
Object方法 | |
clone() | 浅拷贝,只有实现了Cloneable接口才可以调用该方法 |
getClass() | 利用反射机制,根据对象获取Class类型对象 |
toString() | 获取对象的信息,一般都会重写 |
finalize() | 释放资源,很少使用 |
equals() | 比较两个对象是否是同一个,很多类都会重写该方法 |
hashCode() | 用于哈希查找 |
wait() | 使当前线程等待该对象的锁 |
notify() | 唤醒在该对象上等待的某个线程 |
notifyAll() | 唤醒在该对象上等待的所有线程 |
equals原本的方法是比较两个参数引用的是否是同一个对象,即直接比较两个对象的地址是否相等。很多方法重写了equals,变成了比较两个对象的内容是否相等,比如String中就重写了equals方法
- /*
- //类A的定义
- public class A {
- A(int num){
- this.num = num;
- }
- public int num;
- }
- */
- public class Main {
- public static void main(String[] args){
- A a = new A(1);
- A b = new A(1);
- System.out.println(a.equals(b)); //判断a和b引用的是否是同一个对象
-
- String str1 = "abc";
- String str2 = "abc";
- String str3 = "abcd";
- System.out.println(str1.equals(str2)); //判断str1和str2的内容是否相等
- System.out.println(str1.equals(str3)); //判断str1和str3的内容是否相等
- }
- }
输出:
我们可以看到,即使a、b的内容一样, equals也返回了false
hashCode()是用于快速查找的,如果两个对象相同 (equals返回true),那么它们的hashCode值也一定相同;如果hashCode值不同,那么这两个对象一定不相同(equals返回false)。
equals 和 hashCode 的关系
上面的说法可能有点绕,简单来说就是,程序把一个个的对象放在一些桶中,一个桶中可能包含多个对象,每个桶都有一个编号(hashCode),于是我们有以下结论:
- 当两个对象相等时( equals返回true ),它们一定在同一个桶中( hashCode返回值相等 );
- 不在同一个桶中( hashCode返回值不相等 )的两个对象一定不相等( equals返回false );
- 即使两个对象在一个桶中( hashCode返回值相等 ),它们也不一定相等( equals的值不确定 )
于是我们在比较两个对象是否相等时,可以先比较它们hashCode()的值,如果相等,再调用equals去判断;如果hashCode不相等,那么这两个对象一定不相等,就不必再调用equals了
有同学会说为什么要先用hashCode去判断,直接用equals不行吗?
对于数据量比较小的对象,直接用equals当然可以,但你设想一下这种情况:有很多个集合需要判断它们是否相等,每个集合含有的元素个数都非常多,这时候如果一一去调用equals判断就非常慢了,使用hashCode可以加速这个过程,如果两个集合它们的hashCode不相等,那就不用再调用equals去判断了
- import java.util.*;
-
- public class Main {
- public static void main(String[] args){
- List
> list = new ArrayList>(); -
- //初始化,创建20个相同的集合
- for(int i = 0; i <20; i++){
- Set
set = new HashSet(); - for(int j = 0; j <999; j++){
- set.add("set" + j);
- }
- list.add(set);
- }
-
- //给后10个集合添加元素"abc"
- for(int i = 10; i <20; i++){
- list.get(i).add("abc");
- }
-
- //判断后19个集合和第一个集合是否相等
- for(int i = 1; i <20; i++) {
- if(list.get(0).hashCode() == list.get(i).hashCode()) {
- if (list.get(0).equals(list.get(i))) {
- System.out.println("set 0 == set " + i);
- }
- }
- }
- }
- }
Java的反射机制简单来说就是,程序运行时,对任意一个类,都能找出它所有的属性和方法,以及构造它的对象;对任意一个对象,都能调用它的所有方法、获取它的所有属性
反射最常见的应用在IDEA等编译器上,当我们输入一个 类名. ,编译器会弹出该类的所有属性和方法
反射通常和Class类配套使用
假设有一个类A:
- public class A {
- A(int num){
- this.num = num;
- }
- public int num;
- }
a是类A的一个实例:
A a = new A(1);
我们知道a的类型是A类,那么,A的类型是什么?
在Java中,类的类型是 Class 类
创建Class类型对象的方法有三种:
- public class Main {
- public static void main(String[] args) {
- //获取Class类对象的方法一:
- try {
- Class c1 = Class.forName("A");
- System.out.println(c1);
- } catch (ClassNotFoundException error) {
- error.printStackTrace();
- }
-
- //获取Class类对象的方法二:
- Class c2 = A.class;
-
- //获取Class类对象的方法三:
- A a = new A(1);
- Class c3 = a.getClass();
-
- System.out.println(c2);
- System.out.println(c3);
- }
- }
Java的项目有两种配置方式——XML和注解
注解,也叫元数据,即描述数据的数据。简单理解的话,注解就是放在程序段前的一些标注,用来告诉编译器或JVM,这段代码的一些特点、功能或者要执行哪些特别的操作
有些注解在编译之前就被编译器过滤掉了,有些注解能保存到编译之后,还有些注解在运行期间也影响着程序
注解分为三类:标准注解、元注解、自定义注解
标准注解
| @Override | 重写方法 |
@Deprecated | 标明某个类或方法过时 | |
@SuppressWarnings | 标明要忽略的警告 | |
@SafeVarargs | 参数安全类型注解 | |
@FunctionalInterface | 函数接口注解 | |
元注解 (元注解是注解的注解)
| @Retention | 标明注解被保留的阶段 |
@Documented | 标明是否生成Javadoc文档 | |
@Target | 标明注解运用的场景 | |
@Inherited | 标明注解可以被子类继承 | |
@Repeatable | 用于对同一注解多次使用 | |
自定义注解 | \ | \ |