Java是解释型语言。JVM会先把java源代码编译成字节码,若在编译过程中无语法错误,成功生成字节码了,那么代码执行起来就会类似python一样,可能会执行到一半出错卡住。
java程序的入口是main函数,函数声明必须为public static void main(String[] args),该函数可以在任何一个类里面,与类无关,若main函数内需要调用所在类的函数或访问字段,那么需要实例化所在类,或者所访问的东西被修饰为静态的也行。
在命令行运行Java程序时,先用javac编译源代码为class字节码,javacjava complierjava编译器的意思,在使用java来运行字节码。

$javac Main.java
$java Main

args参数可以在命令行运行java程序时赋值,比如:$java test -version,而在main函数中有以下代码:

public class test{
    public static void main(String[] args) {
        for (String arg:args)   if (arg.equals("-version"))
            System.out.println("JDK15.0");
    }
}

那么运行结果为JDK15.0

Java字节码 Bytecode

  C/C++编译器把源代码编译成汇编代码,Java编译器把源代码编译成字节码bytecode。Java跨平台就是基于相同的字节码规范做不同平台的虚拟机,Java程序编译成bytecode后就可以在不同的平台上跑了,也就是说在任何平台上Java源码编译后生成的字节码是一样的,只需处理字节码在不同的系统上的处理机制即可。字节码是Java程序的中间表示形式,正如汇编程序是C或C++程序的中间表示一样。

Java包

为了更好的组织类,Java提供了包机制。包是类的容器,就是一个类库,用于分隔类名空间。如果没有指用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。定包名,所有示例都属于一个默认的无名包。Java中的包一般包含相关的类。Java包类库由一组支持程序开发的类组成。一个编译器或开发环境以一个类库为基础。

API

Application Programming Interface,应用程序接口,是一些预先定义的接口,或指软件系统不同组成部分衔接的约定。用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。

Java基本类型和包装类

Java包含基本类型和包装类,包装类可以有对象,通过new关键字来创建对象并存储在堆中,引用放在栈中,通过引用来使用该实例。基本类型不是类,基本类型可以直接声明,初始化,赋值使用,不需要用new,而且存储在栈中而不是堆中。包装类型相当于将基本类型包装起来。
Java中有8中基本类型:boolean, byte -> short char -> int -> long -> float -> double
1字节:byte
2字节:char short //Java中char占两个字节,采用unicode编码,C++中char占1个,采用ASCII编码(Unicode=UTF-16)
4字节:int
8字节:long float
boolean所占内存大小是不确定的。

包装类就是所谓的类,是一个模板,它描述一类对象的行为和状态。方法就是行为,一个类可以有很多方法,对对象的实例变量的修改都是在方法中进行的。对象是类的一个实例,是实际存在的,不是模板,有状态和行为。实例变量描述一个对象的状态,每个对象都有独特的实例变量。

Java Object类

Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法。
Object 类位于 java.lang 包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。

Object();   // 创建一个新对象
数组

[]表示数组的意思
数组是一个重要的数据结构,需要使用new关键字来申请,定义。也可以直接{}初始化。
数组是引用类型,本质上也是一个类,继承自Object类。可通过.length获取数组的长度,这是数组的一个属性,java编译器在编译时加上去的,不是方法,也不是字段。java.util.Arrays类提供能够方便操作数组的静态方法。

int[] a = new int[size];
String[][] s = new String[20][];
s[0] = new String[30];
s[0][0] = new String("hello world!");
/* 对于String对象,最好使用new String("hello world!")来定义或者初始化,当然也等价于s[0][0] = "hello world!" */

当数组作为参数时,传的是引用。数组是引用类型。

class TestIt
{
    public static void main ( String[] args )
    {
        int[] myArray = {1, 2, 3, 4, 5};
        ChangeIt.doIt( myArray );
        for(int j=0; j<myArray.length; j++)
            System.out.print( myArray[j] + " " );
        // output: 1 2 3 4 5
    }
}
class ChangeIt
{
    static void doIt( int[] z )
    {
        z = null ;
    }
}

在这个例子中,myArray指向一个数组{1, 2, 3, 4, 5},调用doIt函数后,myArray作为引用传参,相当于创建了一个新的引用,指向的东西和myArray指向的是一样的,可以通过两个引用任意一个对指向的这块数据进行修改,但是如果只是将其中任何一个引用指向别的地方的话,对原来的数据是不进行改变的。一定注意myArray和z是两个引用,指向一个东西,z不是myArray的引用,z指向什么地方与myArray无关。
java.util.Arrays提供的数组操作方法:

import java.util.Arrays;
public static void fill(int[] a, int val);  // 赋值数组a内所有元素内容为val
public static void sort(Object[] a);    //数组排序
public static boolean equals(type[] a, type[] a2); //判断两数组是否相同
public static int binarySearch(Object[] a, Object key); //二分查找,数组必须已经排好序,搜索成功返回下标,搜索失败返回-1
Java 修饰符

修饰符用来定义类、方法或者变量,通常放在语句最前端。Java语言的修饰符主要分为两类:访问修饰符非访问修饰符
访问控制修饰符:default, private, public, protected. 各种访问修饰符的访问权限如下:
java访问修饰符权限

私有访问修饰符private:私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。
共有访问修饰符public:被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

Java 运算符

Java运算符主要分为这几个:算术运算符、关系运算符、位运算符、逻辑运算符、赋值运算符、其他运算符
算数运算符:+ - * / % ++ -- ,其中自加,自减有前缀和后缀之分。
关系运算符:== != >= <= > <
位运算符:& | ^ ~ << >> >>>
逻辑运算符:&& || !
赋值运算符:= += -= *= /= %= >>= <<= &= |= ^=
条件运算符:x = (expression)? value if true:value if false

Java 循环结构

顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,,就需要使用循环结构。
Java主要有三种循环结构:while, do...while, for。不同于C++的是,Java里的循环结构是可以命名的,然后break,continue语句可以选择性的跳出具体哪一层循环!

first:while (expression is true){
    //statement;
}

second:do {
    //statement
} while (expression is true);

third:for (init; boolean expression; upd statement) {
    //statement
}

//Java增强for循环:表达式需要时数组类型,不会对数组内元素做出改变
for (声明语句 : 表达式) {
    //
}
Java内置数据类型和引用数据类型 Java Number类

Java是强类型语言,所以Java对于数据类型的规范会相对严格。数据类型在计算机语言里面,是对内存位置的一个抽象表达方式,可以理解为针对内存的一种抽象的表达方式。内置数据类型,也就是基本数据类型,其值是存储在内存的栈空间的,而引用类型的引用存放在栈空间,其值是存放在堆空间的,这是内置类型和引用类型最大的区别。引用类型都继承自Object类。
在作为函数参数进行传参时,基本数据类型传的是形参,即函数里复制了一份一模一样的变量,对原来的变量不会做出改变。而引用类型作为参数时,所调用的函数里面只会复制一份一模一样的引用,引用指向的内存所存放的东西是不会复制的,即会对其指向的内容做出实质性的改变。

在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情形。为了解决这个问题,Java 语言为每一个内置数据类型提供了对应的包装类。所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。

编译器可以把内置类型装箱为包装类,也可以把一个对象拆箱为内置类型。包装类和其对应的内置类型在使用方面无差异。

public class Test{

   public static void main(String[] args){
      Integer x = 5;
      x =  x + 10;
      System.out.println(x);     //15
   }
}

x是一个对象,被赋值整形数值,编译器会对x进行装箱。为了使x能够进行算术运算,会对x进行拆箱。
所有的包装类都属于Number类。

Java基础输入和输出

Java的输出方法在System.out类里面。java输出也支持类似于C++输出的格式修饰符。

public class Main {
    public static void main(String[] args) {
        double d = 3.1415926;
        System.out.printf("%.2f\n", d); // 显示两位小数3.14
        System.out.printf("%.4f\n", d); // 显示4位小数3.1416
        System.out.println("END");    // 输出一行 print line的缩写
    }
}

输入比较麻烦,先导入java.util.Scanner类,然后创建该类并传入System.inSystem.out代表标准输出流,而System.in代表标准输入流。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 创建Scanner对象
        System.out.print("Input your name: "); // 打印提示
        String name = scanner.nextLine(); // 读取一行输入并获取字符串
        System.out.print("Input your age: "); // 打印提示
        int age = scanner.nextInt(); // 读取一行输入并获取整数
        System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
    }
}

例中使用了nextLine()函数读取一行字符串和nextInt()函数读取一个整数。

sort函数使用 Comparable接口和Comparator接口

Java中有两个排序方法,java.util.Arrays中的静态方法sort()和java.util.Collections中的静态方法sort()。前者主要对基本类型和引用类型的数组进行排序,后者则是对一些集合框架进行排序(链表,树,哈希表ArrayList,HashSet,HashMap等)。除此区别之外,其余用法完全相同。

sort(byte[] a);            //默认升序排列
sort(byte[] a, int beginIndex, int endIndex);   // 对下标区间[begin,end]内的元素排序
sort(T[], java.util.Comparator<? super T> c);   //根据指定比较器产生的顺序对指定对象数组进行排序
sort(T[], int beginIndex, int endIndex, java.util.Comparator<> super T> c);

sort函数支持自定义排序,通过重写比较器java.util.Comparator可以实现对引用对象数组极性排序。当不重新定义比较器时,sort函数默认升序排列;定义比较器时,只能对引用类型数据进行排列,基本类型需要装箱后才能定义使用比较器。
一般在java.util.Comparator方法中定义三种返回值(1, -1, 0)来进行排序标准的确认。
当确认该排序关系时,返回-1;确认该排序关系的反关系时,返回1;否则返回0。

// 实现对nums中的元素进行升序排列
Integer[] nums = new Integer[100];
java.util.Collections.sort(nums, java.util.Comparator<Integer>(){
    public int compare(Integar a, Integer b) {
        if (a>b) return -1;
        if (a<b) return 1;
        return 0;
    }
});

在对象数组中也可以通过实现接口来添加排序功能。
让对象继承Comparable接口的方式,称为内部比较器。该对象实现接口Comparable接口,重写compareTo方法。

class student implements java.lang.Comparable{
    int age;
    int sex;
    student(int age, int sex) {
        super();
        this.age = age;
        this.sex = sex;
    }
    public int compareTo(Object o){
        if (o instanceof student) {
            student s = (student)o;
            if (this.age > s.age) {
                return 1;
            }else if (this.age < s.age) {
                return -1;
            }else {
                return 0;
            }
        }
        return 0;
    }
};
// usage
java.util.Arrays.sort(stu);

使用Comparator接口时,称为外部比较器。因为重写其compare方法需要传入两个参数。

class comparable implements java.util.Comparator{
    public int compare(Object o1, Object o2) {
        if (o1 instanceof student) {
            student s1 = (student) o1;
            student s2 = (student) o2;
            return s2.age - s1.age;
        }
        return 0;
    }
};

//usage
java.util.Arrays.sort(stu, new comparable());

下图是两种接口的对比,,,网上拿的图单词打错了(Comparable Comparator)

面向对象 继承

Java中所有类都继承自Object类,且只有该类没有父类。继承关键字为extends,缺省该关键字时编译器会自动加上extends Object。子类称为子类,扩展类;父类称为超类,父类,基类。在Java中一个类只能直接继承一个类,也就是说extends关键字后面只能有一个类名。
子类自动继承父类的所有字段,严禁定义与父类重名的字段。
子类无法访问父类的private字段与方法,这使得继承的作用被削弱了,但是可以访问protected修饰的字段与方法。protected关键字可以把字段和方法的访问权限控制在继承树内部。
supersuper关键字表示父类(超类),子类引用父类的字段时,可以使用super.filedName来访问。在Java中,任何class的构造方法第一个语句都必须是super(),即调用父类的构造方法,没有该语句时编译器会自动添加。若父类没有默认的构造方法,即调用父类的构造方法必须要手动传参,那么就必须在子类中显示调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。还有,子类不会继承父类的任何构造方法,子类的默认构造方法是编译器默认生成的,不是继承的。
super关键字也可以调用父类的方法。
阻止继承Java中final修饰的类不能被继承,即不会有任何子类,final影响继承,但不涉及继承。final修饰的方法不允许被子类覆写。
从Java15开始,允许使用sealed修饰class,并通过permits明确写出能够继承该类的子类。
向上转型与向下转型向上转型即一个引用类型为父类的变量可以指向子类的实例,因为子类拥有父类的全部属性和功能,那么就没有问题。相反,向下转型即一个引用类型为子类的变量指向父类的实例,这是不允许的,Java虚拟机会报ClassCastExpection错误。因为父类无法提供子类所需的全部功能,多的功能无法凭空变出来。
为避免这种错误,转型之前可以使用instanceof操作符判断一下。
instance实际上判断一个变量所指的实例是否为指定类型,或者这个类型的子类。
Java14之后,可以在判断类型一致时直接创建新的引用。

public class Main{
    public static void main (String[] args) {
        Object obj = "Hello world!";
        if (obj instanceof String s) {
            ;    // 判断为true时 s已经创建,可以直接使用。s和obj指向同一个对象
        }
    }
}
多态

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
Java中重载函数允许方法名和参数都相同,但返回类型不同的情况。在继承关系中,子类如果定义与父类方法签名完全相同的方法,被称为覆写(override).
多态的特性就是,运行期才能动态决定调用的子类方法。因为只有运行时才能知道变量所指的实际类型。
比如对于一个继承树上的几个类,我们希望把它们放在一个数组里进行操作,就只需要定义变量类型为它们的公共父类类型,而实际使用时,我们并不知道每个变量所指的实际类型,但是调用相同名字的方法时,就可以动态根据每个实际类型自身来实际调用哪个函数。可见,多态是一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改父类的代码。

抽象类 面向抽象编程

由于多态的定义,每个子类都可以定义与父类同名的方法,但是父类的方法实现没有任何实际意义,又不能去掉,否则就不能用父类变量类型来执行子类方法了,就失去多态性了,这就很烦,因为我们只需要父类的这个方法的声明,所以就有了抽象的概念。可以用abstract关键字修饰方法来使其成为一个抽象方法,这样就不需要具体实现这个方法,而仅仅是一个声明。抽象方法必须放在抽象类中,抽象类无法实例化。
抽象类里面可以定义抽象方法,也可以定义非抽象方法,甚至全部都定义成非抽象方法都可以。

public abstract class Test {
    public static void main(String[] args) {
        System.out.println("abstract");
    }
}

当一个类被abstract关键字定义为抽象类时,这个类就只能被用来继承,抽象类强迫其子类实现其声明的所有方法,否则编译报错。抽象方法实际上相当于定义了“规范”。当抽象类的子类并没有实现其方法时,子类仍然是一个抽象类,即抽象类可以继承抽象类。

abstract class Person {
    public abstract void run();
}
class Student extends Person {
    public void run() {
        System.out.println("Student.run");
    }
}
class Teacher extends Person {
    public void run() {
        System.out.println("Teacher.run");
    }
}
Person p1 = new Student();
Person p2 = new Teacher();

可以用抽象类类型去引用具体的子类型的实例,我们对其方法进行调用,并不关心该变量具体的子类型。尽量引用高层类型,避免引用了实际子类型的方式,称之为面向抽象编程。
面向抽象编程的本质:

上层代码只定义规范
不需要子类就可以实现业务逻辑
具体的业务逻辑由不同的子类实现,调用者并不关心。

对于抽象类的非抽象方法,若该类的子类没有覆写此非抽象方法,那么子类就可以直接调用非抽象方法,不用再去实现。就只是简单的继承该方法。

抽象类是事物的抽象,接口是行为的抽象

接口 interface

如果一个抽象类没有字段,所有方法都是抽象方法,那么可以把该抽象类定义为接口,用interface来声明。接口是比抽象类还要抽象的纯抽象接口,接口的所有方法都是默认被public abstract修饰的,写不写都一样。当一个类实现具体的接口时需要用implements关键字。

interface Person {
    void run();
    String getName();
}
class Student implements Person {
    String name;
    @override
    public void run() {
        System.out.println(this.name + " run");
    }
    @override
    public String getName() {
        return this.name;
    }
}

在Java中,一个类只能继承自一个类,但可以实现多个接口。这就可以变相的使Java具有多继承的特性,使用范围为类继承接口的情况。
Java的接口特指interface的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等。

合理设计interfaceabstract class的继承关系,可以充分复用代码。在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:

List list = new ArrayList();    // 用List接口引用具体子类的实例
Collection col = list;         //向上转型
Iterable it = col;            //还可以继续向上转型

default关键字。既然要一个类实现一个接口必须要实现其全部方法,那么我们只要稍稍修改一下一个接口,比如新增加一个方法声明,是不是就还要在其所有子类中一个一个去增加呢,这也太麻烦了,我们可以把该方法用关键字default修饰一下,这样子类就不需要强制实现给方法,可以实现,也可以不实现,我们只需要根据情况在需要的子类中实现该方法即可。
因为interface中没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。

Java泛型

Java泛型相当于C++里的模板。泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。泛型包括泛型方法和泛型类。
一个泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。定义泛型方法的规则:

所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。

java中泛型标记符:

E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的 java 类型

public class GenericMethodTest {
    public static < E > void printArray( E[] inputArray ) {
        for ( E element : inputArray)
            System.out.printf( "%s ", element );
        System.out.println();
    }
    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组

        System.out.println( "\n双精度型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组

        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    }
}

泛型类:

public class Box<T> {

  private T t;

  public void add(T t) {
    this.t = t;
  }

  public T get() {
    return t;
  }

  public static void main(String[] args) {
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();

    integerBox.add(new Integer(10));
    stringBox.add(new String("菜鸟教程"));

    System.out.printf("整型值为 :%d\n\n", integerBox.get());
    System.out.printf("字符串为 :%s\n", stringBox.get());
  }
}

泛型引用
在使用泛型类时,需要先定义一个泛型类的引用,指定具体的类型。

Score<Integer> score;

也可以不指定具体类型,用通配符?代替,用来表示可以指定任意类型。

Score<?> score;

泛型的界限
在这里需要引入一个重要的概念:泛型的界限。当我们没有指定具体使用的类型时,编译器并不知道具体的类型,那么类型就有了一个界限,比如使用通配符后,类型的上限就是Object类,而此时编译器并不知道应该把这个类声明具体化为哪个类的声明,那么就会自动把生成的对象定义为上限类型Object

也可以指定泛型的界限:extends关键字用来指定上限,super关键字用来指定下限。

public class<T extends Number> {    //设定上界,即类型必须是Number的子类

}

Public class<T super Integer> {    //设定下界,即类型必须是Integer的父类,此时上界仍然是Object

}

在泛型通配符里也可以使用泛型的界限:

Score<? extends Number> score;   //限定为Number的子类

注意指定上界之后,该泛型引用就自动声明为上界类的引用Number

Score<? extends Number> score;   //限定为Number的父类

指定下界后,上界没有变仍然为Object,那么最后生成的引用仍然是Object类型的引用。

枚举类

Java枚举类是一个特殊的类,一般表示一组常量,使用enum关键字来定义,各个常量之间用,隔开。

public enum Color {
    RED, GREEN, BLUE;
    private Color() {
        System.out.println("Constructor called for: " + this.toString());
    }
    public void ColorInfo() {
        System.out.println("Universal Color");
    }
}

枚举类内部的每一个常量都是一个枚举。每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的。枚举类的用处就是使开发者们仅能使用已经定义好的状态。

Color.valueOf("")   //将名称相同的字符串转换为枚举
Color.values()   //快速获取所有的枚举
分类: JAVA SE

0 条评论

发表评论

邮箱地址不会被公开。