初识 java( 8 )15000字详解

在本文中,对之前未涉及的知识点进行讲解

一:scanner

在 Java 中,常用的输入语句是通过使用 Scanner 类来实现的。Scanner 类是 Java 提供的用于读取用户输入的类,它可以从标准输入(键盘)或其他输入流中读取数据。

首先,您需要导入 java.util 包中的 Scanner 类:

import java.util.Scanner;

然后,可以创建一个 Scanner 对象来读取用户的输入:

Scanner scanner = new Scanner(System.in);

下面我来对这句话进行解释:

当我们在 Java 中使用 Scanner 类时,需要先创建一个 Scanner 对象。Scanner scanner = new Scanner(System.in); 表示创建了一个名为 scanner 的 Scanner 对象,并将其与标准输入流 System.in 关联起来。

通过将 System.in 传递给 Scanner 构造函数,我们告诉 Scanner 对象要从标准输入读取数据。标准输入是指通过控制台进行输入的数据源。

创建了 Scanner 对象后,我们可以使用其提供的方法来读取不同类型的输入。

请注意,在使用完 Scanner 对象后,我们应该调用其 close() 方法来释放资源,例如:scanner.close()。这样可以确保程序在不再需要 Scanner 对象时能够正确关闭。

1.1 next的各类方法

在Java中,Scanner类提供了一些方法用于从输入流中读取不同类型的数据。让我们逐个讨论这些方法在遇到换行符和空格时的行为:

  1. nextInt():

    • nextInt(): 读取下一个整数。
    • 从非换行符和非空格字符开始读取,当遇到换行符( /n )或空格时,nextInt()方法将停止读取
    • 如果输入流中的下一个标记不是整数,则会抛出InputMismatchException异常。
  2. nextDouble():

    • nextDouble(): 读取下一个浮点数。
    • 从非换行符和非空格字符开始读取,当遇到换行符或空格时,nextDouble()方法将停止读取
    • 如果输入流中的下一个标记不是表示双精度数的字符序列,则会抛出InputMismatchException异常。
  3. nextBoolean():

    • nextBoolean(): 读取下一个布尔值。
    • 从非换行符和非空格字符开始读取,当遇到换行符或空格时,nextBoolean()方法将停止读取
    • 该方法将读取输入流中的下一个标记并尝试将其解析为布尔值。如果无法解析,则会抛出InputMismatchException异常。
  4. nextLine():

    • nextLine(): 读取下一行文本。
    • 从非换行符开始读取nextLine()方法会读取输入流中的一行文本,能够读取空格,但是遇到换行符停止读取
    • 它将返回包含行文本的字符串,不会跳过任何空格或换行符。

这些方法会根据数据类型来解析输入,并返回相应的值。例如,scanner.nextInt() 会读取下一个整数,并将其作为返回值。

1.2 输出输出缓冲区

在Java中,输入缓冲区是用来提高从输入流中读取数据性能的一种机制。它通过在内存中创建一个缓冲区来存储待读取的数据,并从缓冲区中逐一读取数据,从而减少对底层输入流的频繁访问,提高读取数据的效率。

下面是一个简单的示意图来说明输入缓冲区的使用:

在这里插入图片描述

当我们从键盘输入1234回车的时候,我们会将1234\n输入到缓冲区,接着我们可以通过nextInt()来读取数据,nextInt 从非换行符和非空格字符开始读取,当遇到换行符( /n )或空格时,nextInt()方法将停止读取,所以nextInt会读取到1234,并将这个数字作为返回值返回

从这个示意图可以看出,应用程序不直接从输入流中读取数据,而是通过输入缓冲区来获取数据。当应用程序需要一部分数据时,输入缓冲区会从输入流中读取足够的数据并存储在缓冲区中。应用程序则从输入缓冲区中逐一读取数据。当输入缓冲区中的数据被读取完毕后,输入缓冲区会再次从输入流中读取足够的数据。这种机制避免了频繁读取底层输入流的操作,提高了读取数据的效率。

1.3 hasNext的各种方法

Scanner 类中的 hasNext() 方法有多个重载形式,用于判断输入流中是否还有下一个元素。下面是各种形式的 hasNext() 方法的详细说明以及示例代码:

  1. boolean hasNextBoolean():判断输入流中下一个标记是否为一个 boolean 值。返回一个 boolean 值,当且仅当下一个标记为 true 或 false 时,才返回 true。

  2. boolean hasNextInt():判断输入流中下一个标记是否为一个整数值。返回一个 boolean 值,当且仅当下一个标记为整数时,才返回 true。

  3. boolean hasNextDouble():判断输入流中下一个标记是否为一个浮点数值(double)。返回一个 boolean 值,当且仅当下一个标记为浮点数时,才返回 true。

除了上述的 hasNext() 方法及其各种重载形式外,Scanner 类还提供了其他用于判断输入流中下一个标记的方法,如 hasNextByte()hasNextShort()hasNextLong()hasNextFloat() 等,用法类似。这些方法可以根据需要选择和使用。

我们可以通过hasNext来配合next的各类方法来使用,多了一种检验会更加安全

二:for - each循环

当我们需要迭代遍历一个集合或数组时,Java提供了for-each循环,也称为增强型for循环。它提供了一种更简洁和易读的方式来遍历数据结构,而不必使用传统的for循环和索引。

for-each循环的语法如下:

for (elementDataType element : collection) {
    // 在这里执行需要针对每个元素执行的操作
}

其中,elementDataType是要遍历集合中元素的数据类型,element是一个迭代的临时变量,用于引用每个集合中的元素,collection是要遍历的集合名字。

以下是一个示例程序来演示如何使用for-each循环遍历一个整型数组并打印每个元素的值:

public class ForEachExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        
        for (int x : numbers) {
            System.out.println(number);
        }
    }
}

上述代码中,我们定义了一个整型数组numbers,然后使用for-each循环遍历该数组。在每次迭代中,将当前元素赋值给number变量,并通过System.out.println()打印出来。

这段代码的输出将是:

1
2
3
4
5

这个例子展示了如何使用for-each循环遍历数组。同样的方式也适用于其他类型的集合,比如使用List集合或Set集合。

需要注意的是,for-each循环是只读的,无法修改集合中的元素。如果需要修改集合中的元素,应该使用传统的for循环并结合索引操作。

三:JVM内存分布

在这里插入图片描述

  • 方法区和堆中的内存是所有线程共享的数据区
  • 虚拟机栈,本地方法栈和程序计数器是线程隔离的数据区

接着我们对每个内存的作用做出解释:

  • 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
  • 虚拟机栈(JVM Stack):与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
  • 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
  • 堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
  • 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域

四:get和set方法

在Java中,get和set方法是一种常见的命名规范,用于访问和修改一个类的私有属性。这种命名规范被广泛应用于面向对象编程中,被称为"getter"和"setter"方法。

基本格式:

  • get方法:用于获取私有属性的值,通常以"get"开始,后面跟着要获取属性的名称,没有参数,返回属性的值。

    public dataType getPropertyName() {
        return propertyName;
    }
    
  • set方法:用于修改私有属性的值,通常以"set"开始,后面跟着要设置属性的名称,接受一个参数,参数类型与属性类型相同或兼容。

    public void setPropertyName(dataType value) {
        propertyName = value;
    }
    

这两个方法的存在意义:

  1. 封装数据:通过将属性设置为私有(private)的,可以保护数据不被外部直接访问和修改。只能通过get和set方法来访问和修改属性的值,这样可以控制数据的安全性。
  2. 控制访问权限:通过定义set方法,可以对属性的值进行限制,比如进行合法性检查和处理。同时,只提供get方法可以只允许读取属性的值,而不允许修改,提高代码的可控性和灵活性。
  3. 面向对象原则:get和set方法符合面向对象编程的封装原则,将数据和操作数据的行为封装在同一个类中,提供了一种面向对象的访问方式。

示例代码:

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

    public String getName() {
        return name;
    }

    public void setName(String value) {
        if (value != null && !value.isEmpty()) {
            name = value;
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int value) {
        if (value >= 0 && value <= 150) {
            age = value;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("John");
        person.setAge(25);

        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
    }
}

以上代码中,Person类中定义了name和age两个私有属性,并通过get和set方法进行访问和修改。setName方法检查传入的值是否为空或空字符串,setAge方法检查年龄是否在有效范围内。在Main类中,我们创建了一个Person对象,使用set方法设置属性的值,然后使用get方法获取属性的值并打印输出。

这样的设计可以确保对属性的访问和修改通过统一接口进行,同时可以对属性值进行限制和检查,提高代码的健壮性和可扩展性。

五:匿名内部类和匿名对象

5.1匿名内部类

匿名内部类、匿名对象和匿名方法在Java中都是用来简化代码编写的特殊语法结构。

  1. 匿名内部类:匿名内部类是指在创建对象的同时定义一个类,但是没有对该类进行命名。它的语法形式可以类比于创建接口的实现类的方式。

    interface Animal {
        void sound();
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal animal = new Animal() {
                @Override
                public void sound() {
                    System.out.println("The animal makes a sound.");
                }
            };
            animal.sound();
        }
    }
    

在匿名内部类中, new Animal()代表的是实例化一个实现了Animal接口的对象,接着我们直接在后面接上{ },在里面实现方法的重写即可,因为new会返回这个对象的地址,所以我们把它赋值给Anmial类型的引用即可,这里发生了向上转型,因为如果一个类实现了一个接口,那么这个类和这个接口之间算是一种特殊的父类子类关系,当我们用一个接口类型的引用去指向一个实现了这个接口的类(在这里是匿名内部类,没有名字而已,本质上还是一个类),此时也算是一种向上转型。

当然,上述代码还可以写出这样:

interface Animal {
    void sound();
}

public class Main {
    public static void main(String[] args) {
      new Animal() {
            @Override
            public void sound() {
                System.out.println("The animal makes a sound.");
            }
        }.sound();
    }

这样做的结果是一样的,它会输出 “The animal makes a sound.”,这样做可以直接在创建匿名内部类的同时调用其方法,而不需要额外的引用变量。

上述两端代码和这段代码都是等效的:

interface Animal {
    void sound();
}

class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("The dog makes a sound.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.sound();
    }
}

5.2 匿名对象

  1. 匿名对象:匿名对象是指创建对象时,没有给对象命名,而是直接将对象赋值给一个引用变量或者作为方法的参数传递。它的主要作用是临时使用,不需要重复使用该对象。

    public class Main {
        public static void printMessage() {
            System.out.println("This is a message.");
        }
    
        public static void main(String[] args) {
            printMessage(); // 调用方法时创建匿名对象
            new Main().printMessage(); // 创建对象时创建匿名对象并调用方法
        }
    }
    

    在上述代码中,使用匿名对象来调用printMessage方法,且在创建对象时直接创建了匿名对象并调用方法。通过匿名对象,我们可以在不创建额外的对象的情况下使用对象的方法,从而简化了代码的编写。

5.3 Lambda表达式

Lambda表达式是Java 8中引入的一个重要特性,它允许我们以更简洁的方式实现函数式接口(只包含一个抽象方法的接口),我们可以采用Lambda表达式对匿名内部类进行简写

interface Animal {
    void sound();
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal() {
            @Override
            public void sound() {
                System.out.println("The animal makes a sound.");
            }
        };
        animal.sound();
    }
}

使用Lambda表达式可以将上述代码简化为以下形式:

interface Animal {
    void sound();
}

public class Main {
    public static void main(String[] args) {
        Animal animal = () -> {
            System.out.println("The animal makes a sound.");
        };
        animal.sound();
    }
}

我们来看一下Lambda表达式的语法和格式:

  1. Lambda表达式的基本语法为:(参数列表) -> { 方法体 }
  2. 参数列表可以为空,也可以包含一个或多个参数。在这个例子中,参数列表为空。
  3. ->箭头用于将参数列表和方法体分隔开。
  4. 方法体可以是一个表达式(没有花括号)或一个代码块(有花括号)。
  5. 如果方法体只有一行代码,可以省略花括号。(最好不要省)
  6. Lambda表达式只适用于函数式接口,即匿名内部类中只能有一个抽象方法

六:toString,equals,compareTo,clone

6.1 toString方法

在Java中,toString()是一个非常常用的方法,它是Object类的一个实例方法。默认情况下,当我们调用一个对象的toString()方法时,它会返回一个字符串,该字符串由对象的类名、@符号和对象的哈希码组成。然而,通常我们需要重写这个方法来返回一个更有意义的字符串表示对象的内容。

要重写toString()方法,我们只需要在我们的类中定义这个方法,并返回我们希望的字符串表示。让我们通过一个例子来说明:

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

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

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    public static void main(String[] args) {
        Person person = new Person("John Doe", 30);
        System.out.println(person);
    }
}

在上面的例子中,我们定义了一个Person类,并在类中重写了toString()方法。重写后的toString()方法返回了一个以"name"和"age"的值为内容的字符串。最后,在main方法中创建一个Person对象,并调用toString()方法打印出字符串表示(打印引用类型的对象的时候会自动调用toString方法)

运行上述代码,将会输出以下内容:

Person [name=John Doe, age=30]

正如你所看到的,通过重写toString()方法,我们能够自定义一个表示对象内容的字符串。

toString()方法是Object类的一个公共方法,意味着在任何一个类中都可以直接调用该方法。由于所有的类都继承自Object类,所以每个类都有toString()方法。这也是为什么我们可以在上面的例子中直接调用toString()方法。

6.2equals方法

当我们在Java中比较两个对象是否相等时,可以使用equals()方法。equals()方法是Object类中定义的方法,所有的Java类都直接或间接继承自Object类,因此所有的对象都可以调用该方法。

在Object类中,equals()方法的默认实现是比较两个对象的引用是否相等,即判断两个对象是否是同一个实例。有时候这不符合我们的需求,因为我们可能希望比较的是对象的属性值是否相等。

为了实现自定义的相等比较,我们可以重写equals()方法。在重写equals()方法时,我们应该遵循以下几个约定:

  1. 对称性:如果两个对象相互比较,结果应该是一致的。
  2. 反射性:任何非null的对象与自身比较,结果应该为true。
  3. 传递性:如果对象A与对象B相等,对象B与对象C相等,那么对象A与对象C也应该相等。
  4. 一致性:对于不变的对象,多次调用equals()方法应该返回一致的结果。

下面是一个示例代码,说明如何重写equals()方法:

class Person {
    private String name;
    private int age;
    
	public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person other = (Person) obj;
        return age == other.age && Objects.equals(name, other.name);
    }
}
  1. 对称性

首先,在equals()方法中,我们使用以下代码来检查两个对象是否引用了同一个对象,这个对应了对称性:

if (this == obj) {
    return true;
}

这是一种优化机制,如果两个对象在内存中的引用是相同的,则它们肯定相等。

  1. 反射性:

接下来,我们使用以下代码来检查传入的对象是否为空或者该对象的类与当前对象的类不同,这个对应了反射性,并保证对比的两个对象要是同类型的:

if (obj == null || getClass() != obj.getClass()) {
    return false;
}

这是为了确保我们正在比较同一种类型的对象。如果传入的对象是null或者传入的对象不是当前对象所属的类的实例,那么它们肯定不相等。这个检查确保了比较的准确性和健壮性。

然后,我们将传入的对象强制转换为Person类的实例,以便可以比较它们的属性:

Person other = (Person) obj;

最后,我们使用以下代码来比较两个Person对象的name和age属性:

return age == other.age && Objects.equals(name, other.name);

如果两个Person对象的name和age属性都相等,那么它们被认为是相等的,equals()方法将返回true;否则,它们被认为是不相等的,equals()方法将返回false。

6.3compareTo方法

Java中的compareTo方法是一个用于比较两个对象的方法,它通常用于排序和排序相关的操作。它是Comparable接口中的一个方法。

Comparable接口是一个泛型接口,定义了一个compareTo方法,用于比较两个对象的顺序。这个接口的目的是使类的对象具有可比较性,以便在各种排序算法中使用。

要实现Comparable接口,需要进行以下步骤:

  1. 在类的声明中指定类实现Comparable接口,如下所示:
public class MyClass implements Comparable<MyClass> {
    // 类的成员和方法
}
  1. 实现Comparable接口后,需要重写compareTo方法,根据对象的特定属性或条件来定义对象之间的比较规则。compareTo方法应返回一个整数值,用于表示两个对象之间的关系。
@Override
public int compareTo(MyClass other) {
    // 定义比较规则的逻辑
}

在compareTo方法的实现中,常见的比较逻辑有:

  • 如果当前对象小于other对象,应返回负整数;
  • 如果当前对象等于other对象,应返回零;
  • 如果当前对象大于other对象,应返回正整数。

以下是一个示例代码,演示了如何实现Comparable接口和compareTo方法来对学生对象进行按照学生分数的降序排序:

public class Student implements Comparable<Student> {
    private String name;
    private int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public int compareTo(Student other) {
        // 按分数降序排序
        return other.score - this.score;
    }

    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 85));
        students.add(new Student("Bob", 76));
        students.add(new Student("Charlie", 92));
        
        Collections.sort(students);
        
        for (Student student : students) {
            System.out.println(student.name + ": " + student.score);
        }
    }
}

通过实现Comparable接口和重写compareTo方法,我们可以根据自己定义的比较规则对对象进行排序,从而更好地满足业务需求。

6.4Clone方法

Java中的clone()方法是一种对象复制方法,它允许创建一个对象的精确副本。

clone()方法在Object类中定义,它会复制对象的字段值,并返回克隆后的对象。但需要注意的是,clone()方法是浅拷贝,即只能复制对象本身,而不能复制其所引用的其他对象。

为了使用clone()方法,我们需要让要复制的类实现Cloneable接口。Cloneable接口是一个标记接口,它没有任何方法,但它告诉Java虚拟机可以安全地使用clone()方法来复制该类的对象。

下面是一个简单的示例代码,演示了如何使用clone()方法和实现Cloneable接口:

class Person implements Cloneable {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("John", 25);
        
        try {
            Person person2 = (Person) person1.clone();
            System.out.println("person1: " + person1);
            System.out.println("person2: " + person2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

通过运行上面的代码,您会看到输出结果如下:

person1: Person [name=John, age=25]
person2: Person [name=John, age=25]

可以看到,通过clone()方法,我们成功地创建了person1的副本person2,它们的字段值相同。

七:Object类

Java中的Object类是所有类的根类,它是Java标准库中提供的一个基本类,位于java.lang包下。

所有Java类都是直接或间接地继承自Object类。Object类没有任何直接的父类,它是类继承层次结构的顶层。

Object类的主要用途是作为其他类的基类,提供了一些通用的方法和功能,以帮助其他类实现一些基本的行为和特性。比如上述的toString,equals,compareTo,clone。

在Java中,Object类是所有类的祖先类,因此可以将任何类的对象赋值给Object类型的变量。我们可以用Object来接收任何类型的对象

例如,假设我们有两个类,分别是Person和Car。Person类表示一个人,Car类表示一辆汽车。我们可以创建这两个类的对象,并将它们赋值给Object类型的变量:

Person person = new Person("John");
Car car = new Car("Toyota");

Object obj1 = person; // 通过Person对象赋值给Object类型的变量
Object obj2 = car; // 通过Car对象赋值给Object类型的变量

初始java完结,恭喜你走到这里

送给读者一句话: 轻舟已过万重山

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ice___Cpu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值