BeginnersBook-Java-教程-一-

BeginnersBook Java 教程(一)

原文:BeginnersBook

协议:CC BY-NC-SA 4.0

Java 中的switch-case语句

原文: https://beginnersbook.com/2017/08/java-switch-case/

当我们有多个选项(或选项)时,会使用 Switch case语句,我们可能需要为每个选项执行不同的任务。

Switch case语句的语法如下所示:

switch (variable or an integer expression)
{
     case constant:
     //Java code
     ;
     case constant:
     //Java code
     ;
     default:
     //Java code
     ;
}

Switch Case语句主要用于break语句,即使它是可选的。我们将首先看到一个没有break语句的例子,然后我们将讨论switch casebreak

一个简单的switch case

public class SwitchCaseExample1 {

   public static void main(String args[]){
     int num=2;
     switch(num+2)
     {
        case 1:
	  System.out.println("Case1: Value is: "+num);
	case 2:
	  System.out.println("Case2: Value is: "+num);
	case 3:
	  System.out.println("Case3: Value is: "+num);
        default:
	  System.out.println("Default: Value is: "+num);
      }
   }
}

输出:

Default: Value is: 2

说明:switch中我给出了一个表达式,你也可以给变量。我给了num + 2,其中num值是 2,并且在添加之后表达式得到 4.由于没有用值 4 定义的情况,所以执行了默认情况。这就是为什么我们应该在switch case中使用default,这样如果没有匹配条件,则执行默认块。

switch case流程图

首先,求值在switch括号中提供的变量,值或表达式,然后根据结果,执行与结果匹配的相应case块。

switch case flow diagram

switch-case中的break语句

在 switch case 中,break 语句是可选的,但几乎每次处理 switch case 时都会使用它。在我们讨论 break 语句之前,让我们看看下面的例子,我没有使用 break 语句:

public class SwitchCaseExample2 {

   public static void main(String args[]){
      int i=2;
      switch(i)
      {
	 case 1:
	   System.out.println("Case1 ");
	 case 2:
	   System.out.println("Case2 ");
	 case 3:
	   System.out.println("Case3 ");
	 case 4:
           System.out.println("Case4 ");
	 default:
	   System.out.println("Default ");
      }
   }
}

输出:

Case2 
Case3 
Case4 
Default 

在上面的程序中,我们已经将整数值 2 传递给了switch,所以控制切换到了case 2,但是在case 2之后我们没有使用break语句导致流程传递到后续case直到结束。这个问题的解决方案是break语句

当您希望程序流从switch主体中出来时,使用Break语句。每当在switch主体中遇到break 语句时,执行流程将直接从switch中出来,忽略其余的情况。

让我们采用相同的例子,但这次使用break语句。

break语句的示例

public class SwitchCaseExample2 {

   public static void main(String args[]){
      int i=2;
      switch(i)
      {
	 case 1:
	   System.out.println("Case1 ");
	   break;
	 case 2:
	   System.out.println("Case2 ");
	   break;
	 case 3:
	   System.out.println("Case3 ");
	   break;
	 case 4:
           System.out.println("Case4 ");
           break;
	 default:
	   System.out.println("Default ");
      }
   }
}

输出:

Case2

现在您可以看到只有case 2已被执行,其余case被忽略。

为什么我默认后不使用break语句?

控制流本身会在默认情况下从switch中出来,所以我没有使用它,但是如果您仍然想在默认情况下使用break,那么您可以使用它,这样做没有坏处。

有关switch-case的几点意见

1)case并不总是需要顺序 1,2,3 等。它可以在case关键字后面包含任何整数值。此外,case不需要始终按升序排列,您可以根据要求以任何顺序指定它们。

2)您也可以在switch case中使用字符。例如:

public class SwitchCaseExample2 {

   public static void main(String args[]){
      char ch='b';
      switch(ch)
      {
	 case 'd':
	   System.out.println("Case1 ");
	   break;
	 case 'b':
	   System.out.println("Case2 ");
	   break;
	 case 'x':
	   System.out.println("Case3 ");
	   break;
	 case 'y':
           System.out.println("Case4 ");
           break;
	 default:
	   System.out.println("Default ");
      }
   }
}

3)switch内部给出的表达式应该是一个常量值,否则它将无效。

例如:

switch的有效表达式:

switch(1+2+23)
switch(1*2+3%4)

无效的switch表达式:

switch(ab+cd)
switch(a+b+c)

4)允许嵌套switch语句,这意味着你可以在另一个switch内部使用switch语句。但是应该避免使用嵌套的switch语句,因为它会使程序更复杂,更不易读。

看看这些相关的 java 程序

  1. Java 程序:使用Switch Case 检查字符是元音还是辅音
  2. Java 程序:使用Switch Case制作简单计算器

Java 中的for循环

原文: https://beginnersbook.com/2015/03/for-loop-in-java-with-example/

循环用于重复执行一组语句,直到满足特定条件。在 Java 中,我们有三种类型的基本循环:forwhiledo-while。在本教程中,我们将学习如何在 Java 中使用for循环。

for循环的语法:

for(initialization; condition ; increment/decrement)
{
   statement(s);
}

for循环的执行流程

当程序执行时,解释器总是跟踪将要执行的语句。我们将其称为控制流程或程序的执行流程。

for loop Java

第一步:在for循环中,初始化只发生一次,这意味着for循环的初始化部分只执行一次。

第二步:在每次迭代时求值for循环中的条件,如果条件为真则执行循环体内的语句。一旦条件返回falsefor循环中的语句就不会执行,并且控制在for循环后被转移到程序中的下一个语句。

第三步:每次执行for循环体后,for循环的递增/递减部分执行更新循环计数器

第四步:第三步后,控制跳转到第二步,重新求值条件。

简单的for循环的示例

class ForLoopExample {
    public static void main(String args[]){
         for(int i=10; i>1; i--){
              System.out.println("The value of i is: "+i);
         }
    }
}

该程序的输出是:

The value of i is: 10
The value of i is: 9
The value of i is: 8
The value of i is: 7
The value of i is: 6
The value of i is: 5
The value of i is: 4
The value of i is: 3
The value of i is: 2

在上面的程序中:

  • int i = 1是初始化表达式
  • i > 1是条件(布尔表达式)
  • i--递减运算

无限循环

布尔表达式和递增/递减操作协调的重要性:

class ForLoopExample2 {
    public static void main(String args[]){
         for(int i=1; i>=1; i++){
              System.out.println("The value of i is: "+i);
         }
    }
}

这是一个无限循环,因为条件永远不会返回false。初始化步骤是将变量i的值设置为 1,因为我们正在递增i的值,它总是大于 1(布尔表达式:i > 1)所以它永远不会返回false。这最终会导致无限循环条件。因此,重要的是看到布尔表达式和递增/递减操作之间的协调以确定循环是否将在某个时间点终止。

这是无限for循环的另一个例子:

// infinite loop
for ( ; ; ) {
    // statement(s)
}

for循环迭代数组的示例:

这里我们使用for循环迭代并显示数组元素。

class ForLoopExample3 {
    public static void main(String args[]){
         int arr[]={2,11,45,9};
         //i starts with 0 as array index starts with 0 too
         for(int i=0; i<arr.length; i++){
              System.out.println(arr[i]);
         }
    }
}

输出:

2
11
45
9

增强for循环

当您想要迭代数组/集合时,增强的for循环很有用,它易于编写和理解。

让我们采用上面写的相同的例子,并使用增强的for循环重写它。

class ForLoopExample3 {
   public static void main(String args[]){
      int arr[]={2,11,45,9};
      for (int num : arr) {
         System.out.println(num);
      }
   }
}

输出:

2
11
45
9

注意:在上面的例子中,我在增强的for循环中声明了numint。这将根据数组的数据类型而改变。例如,字符串类型的增强for循环如下所示:

String arr[]={"hi","hello","bye"};
for (String str : arr) {
         System.out.println(str);
}

查看与 for 循环相关的这些 java 编程实例

  1. Java 程序:使用for循环查找自然数之和
  2. Java 程序:使用循环查找数字的阶乘
  3. Java 程序:使用for循环打印 Fibonacci 序列

Java 中的while循环

原文: https://beginnersbook.com/2015/03/while-loop-in-java-with-examples/

在上一个教程中,我们讨论了for循环 。在本教程中,我们将讨论while循环。如前一个教程中所讨论的,循环用于重复执行一组语句,直到满足特定条件。

while循环的语法

while(condition)
{
   statement(s);
}

while循环如何工作?

while循环中,首先计算条件,如果它返回true,则while循环中的语句执行。当条件返回false时,控制流退出循环并跳转到while循环后的下一个语句。

注意:使用while循环时要注意的重点是我们需要在while循环中使用递增或递减语句,以便循环变量在每次迭代时都会更改,并且在某些情况下返回false。这样我们就可以结束while循环的执行,否则循环将无限期地执行。

while loop java

简单的while循环示例

class WhileLoopExample {
    public static void main(String args[]){
         int i=10;
         while(i>1){
              System.out.println(i);
              i--;
         }
    }
}

输出:

10
9
8
7
6
5
4
3
2

无限循环

class WhileLoopExample2 {
    public static void main(String args[]){
         int i=10;
         while(i>1)
         {
             System.out.println(i);
              i++;
         }
    }
}

这个循环永远不会结束,它是一个无限循环。这是因为条件是i > 1,因为我们在循环中递增i的值,所以它总是为真。

这是无限while循环的另一个例子:

while (true){
    statement(s);
}

示例:使用while循环迭代数组

这里我们使用while循环迭代并显示数组元素。

class WhileLoopExample3 {
    public static void main(String args[]){
         int arr[]={2,11,45,9};
         //i starts with 0 as array index starts with 0 too
         int i=0;
         while(i<4){
              System.out.println(arr[i]);
              i++;
         }
    }
}

输出:

2
11
45
9

查看这些相关的程序

  1. Java 程序:使用while循环显示 Fibonacci 序列
  2. Java 程序:使用while循环查找阶乘

Java 中的do-while循环

原文: https://beginnersbook.com/2015/03/do-while-loop-in-java-with-example/

在上一个教程中,我们讨论了while循环。在本教程中,我们将讨论 java 中的do-while循环。do-while循环类似于while循环,但是它们之间存在差异:在while循环中,在执行循环体之前求值条件,但是在执行循环体之后求值do-while循环条件。

do-while循环的语法:

do
{
   statement(s);
} while(condition);

do-while循环如何工作?

首先,循环内的语句执行,然后条件得到求值,如果条件返回true,则控制流转移到do,否则它会在do-while之后跳转到下一个语句。

do while loop java

do-while循环示例

class DoWhileLoopExample {
    public static void main(String args[]){
         int i=10;
         do{
              System.out.println(i);
              i--;
         }while(i>1);
    }
}

输出:

10
9
8
7
6
5
4
3
2

示例:使用do-while循环迭代数组

这里我们有一个整数数组,我们正在迭代数组并使用do-while循环显示每个元素。

class DoWhileLoopExample2 {
    public static void main(String args[]){
         int arr[]={2,11,45,9};
         //i starts with 0 as array index starts with 0
         int i=0;
         do{
              System.out.println(arr[i]);
              i++;
         }while(i<4);
    }
}

输出:
2
11
45
9

Java continue语句

原文: https://beginnersbook.com/2017/08/java-continue-statement/

continue语句主要用于循环内部。每当在循环内遇到它时,控制流直接跳转到循环的开头以进行下一次迭代,跳过循环体内语句的执行以进行当前迭代。当您想继续循环但不希望循环体中的其余语句(在continue语句之后)为该特定迭代执行时,这尤其有用。

语法:

继续单词后跟分号。

continue;

示例:for循环中的continue语句

public class ContinueExample {

   public static void main(String args[]){
	for (int j=0; j<=6; j++)
	{
           if (j==4)
           {
	      continue;
	   }

           System.out.print(j+" ");
	}
   }
}

输出:

0 1 2 3 5 6

您可能已经注意到,输出中缺少值 4,为什么?因为当变量j的值为 4 时,程序遇到 continue语句,这使得它跳转到开头以进行下一次迭代,跳过当前迭代的语句(这就是当j的值为 4 时println没有执行的原因)。

continue语句的流程图

Continue Statement

示例:在while循环中使用continue

你可以在这看到同样的事情。对于counter值,我们将此循环从 10 迭代到 0,当计数器值为 7 时,循环跳过print语句并开始while循环的下一次迭代。

public class ContinueExample2 {

   public static void main(String args[]){
	int counter=10;
	while (counter >=0)
	{
           if (counter==7)
           {
	       counter--;
	       continue;
           }
           System.out.print(counter+" ");
           counter--;
	}
   }
}

输出:

10 9 8 6 5 4 3 2 1 0

do-while循环中continue的示例

public class ContinueExample3 {

   public static void main(String args[]){
	int j=0;
        do
	{
	   if (j==7)
	   {
		 j++;
		 continue;
	   }
	   System.out.print(j+ " ");
	   j++;
       }while(j<10);

   }
}

输出:

0 1 2 3 4 5 6 8 9 

Java 中的break语句

原文: https://beginnersbook.com/2017/08/java-break-statement/

break语句通常用于以下两种情况:

a)使用break语句立即退出循环。每当在循环内遇到break语句时,控制流直接退出循环,并且循环在其余迭代中终止。它与if语句一起使用,只要在循环内部使用,以便循环在特定条件下终止。

这里要注意的重点是,当在嵌套循环中使用break语句时,只有内部循环才会终止。

b)它也用于switch-case控制。通常情况下,switch语句中的所有情况都会跟一个break语句,这样每当程序控制流跳转到一个case时,它就不会执行后续的情况(参见下面的例子)。一旦在switch-case块中遇到中断,控制器就会从switch-case体中出来。

break语句的语法:

break一词后跟分号。

break;

示例 - 在while循环中使用break

在下面的例子中,我们有一个while循环从 0 运行到 100 但由于我们有一个break语句只在循环值达到 2 时发生,循环终止并且控制传递给下一个循环体后的程序语句。

public class BreakExample1 {
   public static void main(String args[]){
      int num =0;
      while(num<=100)
      {
          System.out.println("Value of variable is: "+num);
          if (num==2)
          {
             break;
          }
          num++;
      }
      System.out.println("Out of while-loop");
  }
}

输出:

Value of variable is: 0
Value of variable is: 1
Value of variable is: 2
Out of while-loop

示例 - 在for循环中使用break

你可以在这看到同样的事情。一旦var值达到 99,for循环就会终止。

public class BreakExample2 {

   public static void main(String args[]){
	int var;
	for (var =100; var>=10; var --)
	{
	    System.out.println("var: "+var);
	    if (var==99)
	    {
	         break;
	    }
	 }
	 System.out.println("Out of for-loop");
   }
}

输出:

var: 100
var: 99
Out of for-loop

示例 - 在switch-case中使用break语句

public class BreakExample3 {

   public static void main(String args[]){
	int num=2;

	switch (num)
	{
	    case 1:
	       System.out.println("Case 1 ");
	       break;
	    case 2:
	       System.out.println("Case 2 ");
	       break;
	    case 3:
	       System.out.println("Case 3 ");
	       break;
	    default:
	       System.out.println("Default ");
	}
   }
}

输出:

Case 2

在这个例子中,我们在每个Case块之后都有break语句,这是因为如果我们没有它,那么后续的case块也会执行。不中断的同一程序的输出将是Case 2 Case 3 Default

Java OOP 教程

Java 中的构造函数 - 一个完整的研究

原文: https://beginnersbook.com/2013/03/constructors-in-java/

构造函数是一个初始化新创建对象的代码块。构造函数类似于 java 中的实例方法,但它不是一个方法,因为它没有返回类型。简而言之,构造函数和方法是不同的(本指南末尾有更多内容)。人们经常在 Java 中将构造函数称为特殊类型的方法。

构造函数与类具有相同的名称,在 java 代码中看起来像这样。

public class MyClass{
   //This is the constructor
   MyClass(){
   }
   ..
}

请注意,构造函数名称与类名称匹配,并且它没有返回类型。

构造函数如何工作

为了理解构造函数的工作,我们举个例子。假设我们有一个类MyClass
当我们像这样创建MyClass的对象时:

MyClass obj = new MyClass()

new关键字在这里创建类MyClass的对象并调用构造函数来初始化这个新创建的对象。

你可能会在这里丢失一些,因为我没有向你展示任何初始化示例,让我们看看下面的代码:

java 中的一个简单的构造函数程序

这里我们创建了类Hello的对象obj,然后我们显示了对象的实例变量name。正如您所看到的那样,输出是BeginnersBook.com,这是我们在构造函数初始化期间传递给name的内容。这表明当我们创建对象obj时,构造函数被调用了。在这个例子中我们使用了this关键字,它引用了当前对象,在这个例子中是对象obj。我们将在下一个教程中详细介绍this关键字。

public class Hello {
   String name;
   //Constructor
   Hello(){
      this.name = "BeginnersBook.com";
   }
   public static void main(String[] args) {
      Hello obj = new Hello();
      System.out.println(obj.name);
   }
}

输出:

BeginnersBook.com

new keyword invoked the constructor

构造函数的类型

构造函数有三种类型:默认,无参和参数化构造函数和。

types of constructor

默认构造函数

如果您没有在类中实现任何构造函数,Java 编译器会代表您将默认构造函数插入到代码中。此构造函数称为默认构造函数。您不会在源代码(java 文件)中找到它,因为它将在编译期间插入到代码中并存在于.class文件中。该过程如下图所示:

default constructor

如果实现任何构造函数,则不再从 Java 编译器接收默认构造函数。

无参数构造函数:

没有参数的构造函数称为 无参构造函数。签名与默认构造函数相同,但是正文可以具有任何代码,而不像构造函数的主体为空的默认构造函数。

虽然你可能会看到有些人声称默认和无参构造函数是相同的,但实际上它们不是,即使你在类Demo中编写 public Demo(){} 它也无法调用默认构造函数,因为你已经编写了它的代码。

示例:无参构造函数

class Demo
{
     public Demo()
     {
         System.out.println("This is a no argument constructor");
     }
     public static void main(String args[]) {
    	 new Demo();
     }
}

输出:

This is a no argument constructor

参数化构造函数

带参数的构造函数(或者你可以说参数)被称为参数化构造函数

示例:参数化构造函数

在这个例子中,我们有一个带有两个参数idname的参数化构造函数。在创建对象obj1obj2时,我传递了两个参数,以便在创建obj1obj2之后调用此构造函数。

public class Employee {

   int empId;  
   String empName;  

   //parameterized constructor with two parameters
   Employee(int id, String name){  
       this.empId = id;  
       this.empName = name;  
   }  
   void info(){
        System.out.println("Id: "+empId+" Name: "+empName);
   }  

   public static void main(String args[]){  
	Employee obj1 = new Employee(10245,"Chaitanya");  
	Employee obj2 = new Employee(92232,"Negan");  
	obj1.info();  
	obj2.info();  
   }  
}

输出:

Id: 10245 Name: Chaitanya
Id: 92232 Name: Negan

示例 2:参数化构造函数

在这个例子中,我们有两个构造函数,一个默认构造函数和一个参数化构造函数。当我们在使用new关键字创建对象时不传递任何参数时,将调用默认构造函数,但是当您传递参数时,将调用与传递的参数列表匹配的参数化构造函数。

class Example2
{
      private int var;
      //default constructor
      public Example2()
      {
             this.var = 10;
      }
      //parameterized constructor
      public Example2(int num)
      {
             this.var = num;
      }
      public int getValue()
      {
              return var;
      }
      public static void main(String args[])
      {
              Example2 obj = new Example2();
              Example2 obj2 = new Example2(100);
              System.out.println("var is: "+obj.getValue());
              System.out.println("var is: "+obj2.getValue());
      }
}

输出:

var is: 10
var is: 100

如果在类中仅实现参数化构造函数,该怎么办?

class Example3
{
      private int var;
      public Example3(int num)
      {
             var=num;
      }
      public int getValue()
      {
              return var;
      }
      public static void main(String args[])
      {
              Example3 myobj = new Example3();
              System.out.println("value of var is: "+myobj.getValue());
      }
}

输出:会抛出编译错误。原因是,语句Example3 myobj = new Example3()正在调用我们程序中没有的默认构造函数。当您没有在类中实现任何构造函数时,编译器会将默认构造函数插入到您的代码中,但是当您实现任何构造函数时(在上面的示例中,我已经使用int参数实现了参数化构造函数),那么您不会收到默认构造函数通过编译器进入你的代码。

如果我们从上面的代码中删除参数化构造函数,那么程序运行正常,因为编译器会将默认构造函数插入到代码中。

构造函数链接

当构造函数调用同一个类的另一个构造函数时,这称为构造函数链接。在这里阅读更多相关信息

constructor chaining

super()

每当调用子类构造函数时,它都会隐式调用父类的构造函数。您还可以说编译器在子类构造函数的开头插入super();语句。

class MyParentClass {
   MyParentClass(){
	System.out.println("MyParentClass Constructor");
   }
}
class MyChildClass extends MyParentClass{
   MyChildClass() {
	System.out.println("MyChildClass Constructor");
   }
   public static void main(String args[]) {
	new MyChildClass();
   }
}

输出:

MyParentClass Constructor
MyChildClass Constructor

在这里阅读更多关于super关键字的信息

构造函数重载

构造函数重载是一个具有多个具有不同参数列表的构造函数的概念,以这种方式使每个构造函数执行不同的任务。

constructor overloading

有关详细信息,请参阅示例的构造函数重载

Java 复制构造函数

复制构造函数用于将一个对象的值复制到另一个对象。

class JavaExample{  
   String web; 
   JavaExample(String w){  
	web = w;
   }  

   /* This is the Copy Constructor, it 
    * copies the values of one object
    * to the another object (the object
    * that invokes this constructor)
    */
   JavaExample(JavaExample je){  
	web = je.web; 
   }  
   void disp(){
	System.out.println("Website: "+web);
   }  

   public static void main(String args[]){  
	JavaExample obj1 = new JavaExample("BeginnersBook");  

	/* Passing the object as an argument to the constructor
	 * This will invoke the copy constructor
	 */
	JavaExample obj2 = new JavaExample(obj1);  
	obj1.disp();  
	obj2.disp();  
   }  
}

输出:

Website: BeginnersBook
Website: BeginnersBook

快速回顾

  1. 每个类都有一个构造函数,无论它是普通类还是抽象类。
  2. 构造函数不是方法,它们没有任何返回类型。
  3. 构造函数名称应与类名匹配。
  4. 构造函数可以使用任何访问说明符,也可以将它们声明为私有。私有构造函数在 java 中是可能的,但是范围只在类中。
  5. 类似构造函数方法也可以具有与类名相同的名称,但它们仍然具有返回类型,尽管我们可以识别它们是方法而不是构造函数。
  6. 如果你没有在类中实现任何构造函数,编译器将执行它。
  7. this()super()应该是构造函数代码中的第一个语句。 如果您不提及它们,编译器会相应地为您执行此操作。
  8. 构造函数重载是可能的,但是无法覆盖。这意味着我们可以在类中重载构造函数,但是我们不能覆盖构造函数。
  9. 构造函数不能被继承。
  10. 如果超类没有无参(默认)构造函数,那么编译器不会像在正常情况下那样在子类中插入默认构造函数。
  11. 接口没有构造函数
  12. 抽象类可以有构造函数,当实现接口的类被实例化时,它会被调用。 (即具体类的对象创建)。
  13. 构造函数也可以调用同一个类的另一个构造函数 - 使用this()。如果你想调用一个参数化的构造函数,那么就这样做: this(params)

有关构造函数的更多信息:

构造函数和方法之间的区别

我知道我应该在本指南的开头提到它,但我想要涵盖流程中的所有内容。希望你不要介意:)

  1. 构造函数的目的是初始化类的对象,而方法的目的是通过执行 java 代码来执行任务。
  2. 方法可以是构造函数,不能是抽象的,最终的,静态的和同步的。
  3. 当方法有时,构造函数没有返回类型。

Java - 静态类,块,方法和变量

原文: https://beginnersbook.com/2013/04/java-static-class-block-methods-variables/

static关键字可以与类,变量,方法和块一起使用。静态成员属于类而不是特定实例,这意味着如果您将成员设为静态,则可以在不使用对象的情况下访问它。我们举一个例子来理解这个:

这里我们有一个静态方法myMethod(),我们可以在没有任何对象的情况下调用这个方法,因为当我们将一个成员静态化时它就变成了类级别。如果我们删除static关键字并使其成为非静态关键字,那么我们必须创建一个类的对象才能调用它。

静态成员对于类的所有实例(对象)是通用的,但非静态成员对于每个类实例是独立的。

class SimpleStaticExample
{
    // This is a static method
    static void myMethod()
    {
        System.out.println("myMethod");
    }

    public static void main(String[] args)
    {
          /* You can see that we are calling this
           * method without creating any object. 
           */
           myMethod();
    }
}

输出:

myMethod

静态块

静态块用于初始化静态变量。当在内存中加载类时,将执行此块。一个类可以有多个静态块,它们将按照它们写入程序的相同顺序执行。

示例 1:单个静态块

正如您所看到的,在我们在main方法中访问它们之前,两个静态变量都已初始化。

class JavaExample{
   static int num;
   static String mystr;
   static{
      num = 97;
      mystr = "Static keyword in Java";
   }
   public static void main(String args[])
   {
      System.out.println("Value of num: "+num);
      System.out.println("Value of mystr: "+mystr);
   }
}

输出:

Value of num: 97
Value of mystr: Static keyword in Java

示例 2:多个静态块

让我们看看多个静态块如何在 Java 中工作。它们以给定的顺序执行,这意味着第一个静态块在第二个静态块之前执行。这就是原因,第一个块初始化的值被第二个块覆盖。

class JavaExample2{
   static int num;
   static String mystr;
   //First Static block
   static{
      System.out.println("Static Block 1");
      num = 68;
      mystr = "Block1";
  } 
  //Second static block
  static{
      System.out.println("Static Block 2");
      num = 98;
      mystr = "Block2";
  }
  public static void main(String args[])
  {
      System.out.println("Value of num: "+num);
      System.out.println("Value of mystr: "+mystr);
   }
}

输出:

Static Block 1
Static Block 2
Value of num: 98
Value of mystr: Block2

Java 静态变量

静态变量对于类的所有实例(或对象)是通用的,因为它是类级变量。换句话说,您可以说只创建了一个静态变量副本,并在该类的所有实例之间共享。这些变量的内存分配仅在类加载到内存中时才会发生一次。
几点重点:

  • 静态变量也称为类变量。
  • 非静态变量不同,这些变量可以直接在静态和非静态方法中访问。

示例 1:可以在静态方法中直接访问静态变量

这里我们有一个静态方法disp()和两个静态变量var1var2。这两个变量都可以在静态方法中直接访问。

class JavaExample3{
  static int var1;
  static String var2;
  //This is a Static Method
  static void disp(){
      System.out.println("Var1 is: "+var1);
      System.out.println("Var2 is: "+var2);
  }
  public static void main(String args[]) 
  {
      disp();
  }
}

输出:

Var1 is: 0
Var2 is: null

示例 2:静态变量在类的所有实例之间共享

在此示例中,字符串变量是非静态的,整数变量是静态的。正如您在输出中看到的那样,两个对象的非静态变量是不同的,但静态变量在它们之间共享,这就是对象ob2对静态变量所做的更改在两个对象中反映的原因。

class JavaExample{
   //Static integer variable
   static int var1=77; 
   //non-static string variable
   String var2;

   public static void main(String args[])
   {
	JavaExample ob1 = new JavaExample();
	JavaExample ob2 = new JavaExample();
	/* static variables can be accessed directly without
	 * any instances. Just to demonstrate that static variables
	 * are shared, I am accessing them using objects so that 
	 * we can check that the changes made to static variables
	 * by one object, reflects when we access them using other
	 * objects
	 */
        //Assigning the value to static variable using object ob1
	ob1.var1=88;
	ob1.var2="I'm Object1";
        /* This will overwrite the value of var1 because var1 has a single 
         * copy shared among both the objects.
         */
        ob2.var1=99;
	ob2.var2="I'm Object2";
	System.out.println("ob1 integer:"+ob1.var1);
	System.out.println("ob1 String:"+ob1.var2);
	System.out.println("ob2 integer:"+ob2.var1);
	System.out.println("ob2 STring:"+ob2.var2);
   }
}

输出:

ob1 integer:99
ob1 String:I'm Object1
ob2 integer:99
ob2 STring:I'm Object2

有关参考的更多详细信息: Java - 静态变量

Java 静态方法

静态方法可以在不使用类的对象(实例)的情况下访问类变量(静态变量),但是只能使用对象访问非静态方法和非静态变量。

可以在静态和非静态方法中直接访问静态方法。

语法:

静态关键字后跟返回类型,后跟方法名称。

static return_type method_name();

示例 1:静态方法main访问没有对象的静态变量

class JavaExample{
   static int i = 10;
   static String s = "Beginnersbook";
   //This is a static method
   public static void main(String args[]) 
   {
       System.out.println("i:"+i);
       System.out.println("s:"+s);
   }
}

输出:

i:10
s:Beginnersbook

示例 2:直接在静态和非静态方法中访问的静态方法

class JavaExample{
  static int i = 100;
  static String s = "Beginnersbook";
  //Static method
  static void display()
  {
     System.out.println("i:"+i);
     System.out.println("i:"+s);
  }

  //non-static method
  void funcn()
  {
      //Static method called in non-static method
      display();
  }
  //static method
  public static void main(String args[])
  {
	  JavaExample obj = new JavaExample();
	  //You need to have object to call this non-static method
	  obj.funcn();

      //Static method called in another static method
      display();
   }
}

输出:

i:100
i:Beginnersbook
i:100
i:Beginnersbook

阅读更多:静态方法与 Java 中的非静态方法

静态类

只有当它是嵌套类时,才能使静态成为类。

  1. 嵌套的静态类不需要引用外类
  2. 静态类无法访问外部类的非静态成员

我们将在一个例子的帮助下看到这两点:

静态类示例

class JavaExample{
   private static String str = "BeginnersBook";

   //Static class
   static class MyNestedClass{
	//non-static method
	public void disp() {

	   /* If you make the str variable of outer class
	    * non-static then you will get compilation error
	    * because: a nested static class cannot access non-
	    * static members of the outer class.
	    */
	   System.out.println(str); 
	}

   }
   public static void main(String args[])
   {
       /* To create instance of nested class we didn't need the outer
	* class instance but for a regular nested class you would need 
	* to create an instance of outer class first
        */
	JavaExample.MyNestedClass obj = new JavaExample.MyNestedClass();
	obj.disp();
   }
}

输出:

BeginnersBook

Java 编程中的继承

原文: https://beginnersbook.com/2013/03/inheritance-in-java/

一个类获取另一个类的属性(数据成员)和功能(方法)的过程称为继承。继承的目的是提供代码的可重用性,以便类只需要编写唯一的特性,其余的公共属性和功能可以从另一个类扩展。

子类:

扩展另一个类的特性的类称为子类,子类或派生类。

父类:

其属性和功能由另一个类使用(继承)的类称为父类,超类或基类。

继承是通过扩展其公共数据成员和方法,基于现有类定义新类的过程。

继承允许我们重用代码,它提高了 java 应用的可重用性。

注意:继承的最大优势是,基类中已经存在的代码不需要在子类中重写。

这意味着父类的数据成员(实例变量)和方法可以在子类中使用。

如果您发现很难理解什么是类和对象,那么请参考我在面向对象编程上分享的指南:OOP 概念

让我们回到主题:

语法:Java 中的继承

要继承一个类,我们使用extends关键字。这里的类XYZ是子类,类ABC是父类。XYZ类继承了ABC类的属性和方法。

class XYZ extends ABC
{
}

继承示例

在这个例子中,我们有一个基类Teacher和一个子类PhysicsTeacher。由于类PhysicsTeacher扩展了基类的designationcollegeName属性以及does()方法,我们不需要在子类中声明这些属性和方法。

这里我们有collegeNamedesignationdoes()方法,这些方法对于所有老师都是通用的,所以我们在基类中声明了它们,这样像MathTeacherMusicTeacherPhysicsTeacher这样的子类不会需要编写此代码,可以直接从基类中使用。

class Teacher {
   String designation = "Teacher";
   String collegeName = "Beginnersbook";
   void does(){
	System.out.println("Teaching");
   }
}

public class PhysicsTeacher extends Teacher{
   String mainSubject = "Physics";
   public static void main(String args[]){
	PhysicsTeacher obj = new PhysicsTeacher();
	System.out.println(obj.collegeName);
	System.out.println(obj.designation);
	System.out.println(obj.mainSubject);
	obj.does();
   }
}

输出:

Beginnersbook
Teacher
Physics
Teaching

基于上述例子,我们可以说PhysicsTeacher IS-A Teacher。这意味着子类与父类具有 IS-A 关系。这是继承被称为子类和父类之间的 IS-A 关系

注意:

派生类继承声明为publicprotected的所有成员和方法。如果超类的成员或方法声明为private,则派生类不能直接使用它们。私有成员只能在自己的类中访问。只能使用公共或受保护的超类获取器和设置器方法访问此类私有成员,如下例所示。

class Teacher {
   private String designation = "Teacher";
   private String collegeName = "Beginnersbook";
   public String getDesignation() {
	return designation;
   }
   protected void setDesignation(String designation) {
	this.designation = designation;
   }
   protected String getCollegeName() {
	return collegeName;
   }
   protected void setCollegeName(String collegeName) {
	this.collegeName = collegeName;
   }
   void does(){
	System.out.println("Teaching");
   }
}

public class JavaExample extends Teacher{
   String mainSubject = "Physics";
   public static void main(String args[]){
	JavaExample obj = new JavaExample();
	/* Note: we are not accessing the data members 
	 * directly we are using public getter method 
	 * to access the private members of parent class
	 */
	System.out.println(obj.getCollegeName());
	System.out.println(obj.getDesignation());
	System.out.println(obj.mainSubject);
	obj.does();
   }
}

输出是:

Beginnersbook
Teacher
Physics
Teaching

在上面的例子中需要注意的重要一点是,子类能够通过父类的受保护方法访问父类的私有成员。当我们创建一个实例变量(数据成员)或方法受保护时,这意味着它们只能在类本身和子类中访问。这些公共,受保护,私有等都是访问说明符,我们将在接下来的教程中讨论它们。

继承的类型

要详细了解继承类型,请参阅: Java 中的继承类型

单一继承:指的是一个子类和父类关系,其中一个类扩展另一个类。

Single Inheritance

多级继承:指一个类扩展子类的子类和父类关系。例如,C类扩展了B类,B类扩展了A类。

Multilevel Inheritance

分层继承:指的是子类和父类关系,其中多个类扩展同一个类。例如,BCB类。D扩展了相同的A类。

Hierarchical Inheritance

多重继承 :指一个类扩展多个类的概念,这意味着子类有两个父类。例如,类C扩展了类AB. Java 不支持多重继承,请阅读多重继承

Multiple Inheritance

混合继承:在单个程序中组合多种类型的继承。例如A类和A类。B扩展了类C,另一个类D扩展了类A,然后这是一个混合继承示例,因为它是单继承和层次继承的组合。

构造函数和继承

当我们创建子类的对象时,会调用子类的构造函数,它默认调用超类的默认构造函数。因此,在继承中,对象是自上而下构造的。可以使用super关键字显式调用超类构造函数,但它应该是构造函数中的第一个语句。super关键字指的是超类,紧接在层次结构中的调用类之上。不允许使用多个super关键字来访问除直接父级之外的祖先类。

class ParentClass{
   //Parent class constructor
   ParentClass(){
	System.out.println("Constructor of Parent");
   }
}
class JavaExample extends ParentClass{
   JavaExample(){
	/* It by default invokes the constructor of parent class
	 * You can use super() to call the constructor of parent.
	 * It should be the first statement in the child class
	 * constructor, you can also call the parameterized constructor
	 * of parent class by using super like this: super(10), now
	 * this will invoke the parameterized constructor of int arg
	 */
	System.out.println("Constructor of Child");
   }
   public static void main(String args[]){
	//Creating the object of child class
	new JavaExample();
   }
}

输出:

Constructor of Parent
Constructor of Child

继承和方法覆盖

当我们在父类中已经存在的子类中声明相同的方法时,这称为方法覆盖。在这种情况下,当我们从子类对象调用该方法时,将调用该方法的子类版本。但是我们可以使用super关键字调用父类方法,如下例所示:

class ParentClass{
   //Parent class constructor
   ParentClass(){
	System.out.println("Constructor of Parent");
   }
   void disp(){
	System.out.println("Parent Method");
   }
}
class JavaExample extends ParentClass{
   JavaExample(){
	System.out.println("Constructor of Child");
   }
   void disp(){
	System.out.println("Child Method");
        //Calling the disp() method of parent class
	super.disp();
   }
   public static void main(String args[]){
	//Creating the object of child class
	JavaExample obj = new JavaExample();
	obj.disp();
   }
}

输出是:

Constructor of Parent
Constructor of Child
Child Method
Parent Method

Java 基础知识教程

Java 中的继承类型:单一,多重,多级和混合

原文: https://beginnersbook.com/2013/05/java-inheritance-types/

以下是 Java 中的各种类型的继承。我们将在示例和流程图的帮助下逐一看到它们中的每一个。

1)单一继承

单继承很容易理解。当一个类只扩展另一个类时,我们称之为单个继承。下面的流程图显示B类只扩展了一个A类。这里AB父类BA子类

Single Inheritance

Java 中的单一继承示例程序

Class A
{
   public void methodA()
   {
     System.out.println("Base class method");
   }
}

Class B extends A
{
   public void methodB()
   {
     System.out.println("Child class method");
   }
   public static void main(String args[])
   {
     B obj = new B();
     obj.methodA(); //calling super class method
     obj.methodB(); //calling local method
  }
}

2)多重继承

多重继承”是指一个类扩展(或继承)多个基类的概念。我们之前学到的继承具有一个基类或父类的概念。 “多重继承”的问题是派生类必须管理对两个基类的依赖。

Multiple-Inheritance

注 1:在软件项目中很少使用多重继承。使用多重继承通常会导致层次结构中出现问题。当进一步扩展课程时,这会导致不必要的复杂性。

注 2:大多数新的 OO 语言如 Small Talk,Java,C# 不支持多重继承。 C++ 支持多重继承。

3)多级继承

多级继承是指 OO 技术中的一种机制,可以从派生类继承,从而使这个派生类成为新类的基类。正如您在下面的流程图中所见,CB的子类或子类,BA的子类。有关更多详细信息和示例,请参阅 - Java 中的多级继承

Multilevel-Inheritance

Java 中的多级继承示例程序

Class X
{
   public void methodX()
   {
     System.out.println("Class X method");
   }
}
Class Y extends X
{
public void methodY()
{
System.out.println("class Y method");
}
}
Class Z extends Y
{
   public void methodZ()
   {
     System.out.println("class Z method");
   }
   public static void main(String args[])
   {
     Z obj = new Z();
     obj.methodX(); //calling grand parent class method
     obj.methodY(); //calling parent class method
     obj.methodZ(); //calling local method
  }
}

4)分层继承

在这种类型的继承中,一个类由许多子类继承。在下面的例子B类中,CD继承相同的A类.ABCD父类(或基类)。阅读更多 - java 中的分层继承与示例程序

Hierarchical-Inheritance

5)混合继承

简单来说,你可以说混合继承是继承的组合。典型的流程图如下所示。可以在 java 中实现混合继承,就像多继承一样!使用接口。是的,你听到了。通过使用接口,您可以在 Java 中拥有多个以及混合继承

阅读完整的文章 - 使用示例程序在 java 中进行混合继承

Hybrid-inheritance

OOP 概念 - 什么是 java 中的聚合?

原文: https://beginnersbook.com/2013/05/aggregation/

聚合是一种特殊的关联形式。它是关联这两个类之间的关系,然而它是方向关联,这意味着它严格地是单向关联。 它代表 HAS-A 的关系。

Java 中的聚合示例

例如,考虑两个类Student类和Address类。每个学生都有一个地址,所以学生和地址之间的关系是一个 Has-A 关系。但如果你认为反之亦然那么它就没有任何意义,因为Address不一定需要Student。让我们在 java 程序中编写这个例子。

Student Has-A Address

class Address
{
   int streetNum;
   String city;
   String state;
   String country;
   Address(int street, String c, String st, String coun)
   {
       this.streetNum=street;
       this.city =c;
       this.state = st;
       this.country = coun;
   }
}
class StudentClass
{
   int rollNum;
   String studentName;
   //Creating HAS-A relationship with Address class
   Address studentAddr; 
   StudentClass(int roll, String name, Address addr){
       this.rollNum=roll;
       this.studentName=name;
       this.studentAddr = addr;
   }
   public static void main(String args[]){
       Address ad = new Address(55, "Agra", "UP", "India");
       StudentClass obj = new StudentClass(123, "Chaitanya", ad);
       System.out.println(obj.rollNum);
       System.out.println(obj.studentName);
       System.out.println(obj.studentAddr.streetNum);
       System.out.println(obj.studentAddr.city);
       System.out.println(obj.studentAddr.state);
       System.out.println(obj.studentAddr.country);
   }
}

输出:

123
Chaitanya
55
Agra
UP
India

上面的例子显示了StudentAddress类之间的 聚合 。你可以看到在Student类中我已经声明了一个Address类型的属性来获取学生地址。它是 Java 中聚合的典型示例。

为什么我们需要聚合?

维护代码重用性。要理解这一点,请再次使用相同的示例。假设还有另外两个类CollegeStaff以及上面两个类StudentAddress。为了保持学生的地址,学院地址和工作人员的地址,我们不需要一次又一次地使用相同的代码。我们只需要在定义每个类时使用Address类的引用,如:

Student Has-A Address (Has-a relationship between student and address)
College Has-A Address (Has-a relationship between college and address)
Staff Has-A Address (Has-a relationship between staff and address)

因此,我们可以通过使用聚合关系来提高代码的可重用性。

所以,如果我必须在一个程序中写这个,我会这样做:

class Address
{
   int streetNum;
   String city;
   String state;
   String country;
   Address(int street, String c, String st, String coun)
   {
       this.streetNum=street;
       this.city =c;
       this.state = st;
       this.country = coun;
   }
}
class StudentClass
{
   int rollNum;
   String studentName;
   //Creating HAS-A relationship with Address class
   Address studentAddr; 
   StudentClass(int roll, String name, Address addr){
       this.rollNum=roll;
       this.studentName=name;
       this.studentAddr = addr;
   }
   ...
}
class College
{
   String collegeName;
   //Creating HAS-A relationship with Address class
   Address collegeAddr; 
   College(String name, Address addr){
       this.collegeName = name;
       this.collegeAddr = addr;
   }
   ...
}
class Staff
{
   String employeeName;
   //Creating HAS-A relationship with Address class
   Address employeeAddr; 
   Staff(String name, Address addr){
       this.employeeName = name;
       this.employeeAddr = addr;
   }
   ...
}

正如您所看到的,我们没有在三个类中的任何一个中编写Address代码,我们只是使用Address类创建了 HAS-A 关系以使用Address代码。上面代码中的点点(...)部分可以用public static void main方法替换,其中的代码与我们在第一个例子中看到的类似。

OOP 概念 - java 中的关联是什么?

原文: https://beginnersbook.com/2013/05/association/

在本文中,我们将讨论 Java 中的关联。关联通过其对象建立两个独立的之间的关系。这种关系可以是一对一,一对多,多对一,多对多。

关联例子

class CarClass{
   String carName;
   int carId;
   CarClass(String name, int id)
   {
	this.carName = name;
	this.carId = id;
   }
}
class Driver extends CarClass{
   String driverName;
   Driver(String name, String cname, int cid){
	super(cname, cid);
	this.driverName=name;
   }
}
class TransportCompany{
   public static void main(String args[])
   {
	Driver obj = new Driver("Andy", "Ford", 9988);
	System.out.println(obj.driverName+" is a driver of car Id: "+obj.carId);
   }
}

输出:

Andy is a driver of car Id: 9988

在上面的例子中,两个类之间存在一对一的关系( 组合 ):CarClassDriver。这两个类代表两个独立的实体。

关联与聚合与组合

让我们讨论关联,聚合和组合之间的差异:

尽管这三个都是相关的术语,但它们在两个类的关联方式上存在一些主要差异。 关联是两个独立类之间的关系,关联可以是任何类型,例如一对一,一对可以等。它连接两个完全独立的实体。

聚合是一种特殊的关联形式,它是类(或实体)之间的单向单向关系,例如,钱包和金钱课程。钱包有钱,但钱不需要钱包,所以它是一个单向的关系。在这种关系中,如果其他条目结束,这两个条目都可以存活在我们的示例中,如果Wallet类不存在,则并不意味着Money类不存在。

组合是一种限制形式的聚合,其中两个实体(或者你可以说类)高度依赖于彼此。对于例如人与心。人类需要内心生存,心脏需要人体才能生存。换句话说,当类(实体)彼此依赖并且它们的寿命相同时(如果一个人死了,那么另一个也是),那么它就是一个组合。如果没有人类,心课就没有意义。

java 中的super关键字

原文: https://beginnersbook.com/2014/07/super-keyword-in-java-with-example/

super关键字指的是直接父类的对象。在学习super关键字之前,您必须具备 Java 中继承的知识,以便您能够理解本指南中给出的示例。

使用super关键字

1)当父类和子类都具有相同名称的成员时访问父类的数据成员

2)显式调用父类的无参和参数化构造函数

3)访问父类的方法当子类重写该方法时的类。

现在让我们借助示例详细讨论它们:

1)如何使用super关键字访问父类的变量

当子类中的变量已存在于父类中时,为了访问父类的变量,您需要使用super关键字。

让我们举一个例子来理解这一点:在下面的程序中,我们在子类中声明了一个数据成员num,父类中已经存在同名的成员。如果不使用super关键字,则无法访问父类的num变量。 。

//Parent class or Superclass or base class
class Superclass
{
   int num = 100;
}
//Child class or subclass or derived class
class Subclass extends Superclass
{
   /* The same variable num is declared in the Subclass
    * which is already present in the Superclass
    */
    int num = 110;
    void printNumber(){
	System.out.println(num);
    }
    public static void main(String args[]){
	Subclass obj= new Subclass();
	obj.printNumber();	
    }
}

输出:

110

访问父类的num变量:

通过调用这样的变量,如果类(父类和子类)具有相同的变量,我们可以访问父类的变量。

super.variable_name

让我们采用我们在上面看到的相同示例,这次在print语句中我们传递super.num而不是num

class Superclass
{
   int num = 100;
}
class Subclass extends Superclass
{
   int num = 110;
   void printNumber(){
	/* Note that instead of writing num we are
	 * writing super.num in the print statement
	 * this refers to the num variable of Superclass
	 */
	System.out.println(super.num);
   }
   public static void main(String args[]){
	Subclass obj= new Subclass();
	obj.printNumber();	
   }
}

输出:

100

正如您通过使用super.num所看到的,我们访问了父类的num变量。

2)使用super关键字来调用父类的构造函数

当我们创建子类的对象时,new关键字调用子类的构造函数,它隐式调用父类的构造函数。因此,当我们创建子类的对象时执行的顺序是:首先执行父类构造函数,然后执行子类构造函数。这是因为编译器本身添加了super()(这会调用父类的无参数构造函数)作为子类构造函数中的第一个语句。

让我们看一个例子来理解我上面解释的内容:

class Parentclass
{
   Parentclass(){
	System.out.println("Constructor of parent class");
   }
}
class Subclass extends Parentclass
{
   Subclass(){
	/* Compile implicitly adds super() here as the
	 *  first statement of this constructor.
	 */
	System.out.println("Constructor of child class");
   }
   Subclass(int num){
	/* Even though it is a parameterized constructor.
	 * The compiler still adds the no-arg super() here
	 */
	System.out.println("arg constructor of child class");
   }
   void display(){
	System.out.println("Hello!");
   }
   public static void main(String args[]){
	/* Creating object using default constructor. This 
	 * will invoke child class constructor, which will 
	 * invoke parent class constructor
	 */
	Subclass obj= new Subclass();
	//Calling sub class method 
	obj.display();
	/* Creating second object using arg constructor
	 * it will invoke arg constructor of child class which will
	 * invoke no-arg constructor of parent class automatically 
	 */
	Subclass obj2= new Subclass(10);
	obj2.display();
   }
}

输出:

Constructor of parent class
Constructor of child class
Hello!
Constructor of parent class
arg constructor of child class
Hello!

参数化的super()调用来调用父类的参数化构造函数

我们可以在子类的构造函数中显式调用super(),但它没有任何意义,因为它会是多余的。这就像明确地做一些其他方式隐含的事情。

但是当我们在父类中有一个带有参数的构造函数时,我们可以使用参数化的超类,如super(100);从子类的构造函数中调用父类的参数化构造函数

让我们看一个例子来理解这个:

class Parentclass
{
   //no-arg constructor
   Parentclass(){
	System.out.println("no-arg constructor of parent class");
   }
   //arg or parameterized constructor
   Parentclass(String str){
	System.out.println("parameterized constructor of parent class");
   }
}
class Subclass extends Parentclass
{
   Subclass(){
       /* super() must be added to the first statement of constructor 
	* otherwise you will get a compilation error. Another important 
	* point to note is that when we explicitly use super in constructor
	* the compiler doesn't invoke the parent constructor automatically.
	*/
	super("Hahaha");
	System.out.println("Constructor of child class");

   }
   void display(){
	System.out.println("Hello");
   }
   public static void main(String args[]){		
	Subclass obj= new Subclass();
	obj.display();	 
   }
}

输出:

parameterized constructor of parent class
Constructor of child class
Hello

在这个例子中有几点需要注意:

1)super()(或参数化的super必须是构造函数中的第一个语句,否则你将得到编译错误:“构造函数调用必须是构造函数中的第一个语句”

2)当我们在构造函数中明确地放置super时,java 编译器没有调用父类的默认无参构造函数。

3)如何在方法覆盖的情况下使用super关键字

当子类声明父类中已存在的相同方法时,这称为方法覆盖。我们将在本系列的下一个教程中学习方法覆盖。现在您只需要记住这一点:当子类重写父类的方法时,从子类对象调用该方法总是调用该方法的子类版本。但是通过使用这样的super关键字:super.method_name可以调用父类的方法(被覆盖的方法)。在方法覆盖的情况下,使用这些术语:重写方法:父类的方法重写方法:子类的方法让我们举一个例子来理解这个概念:

class Parentclass
{
   //Overridden method
   void display(){
	System.out.println("Parent class method");
   }
}
class Subclass extends Parentclass
{
   //Overriding method
   void display(){
	System.out.println("Child class method");
   }
   void printMsg(){
	//This would call Overriding method
	display();
	//This would call Overridden method
	super.display();
   }
   public static void main(String args[]){		
	Subclass obj= new Subclass();
	obj.printMsg(); 
   }
}

输出:

Child class method
Parent class method

如果子类没有覆盖任何方法怎么办:不需要super

当子类没有覆盖父类方法时,我们不需要使用super关键字来调用父类方法。这是因为在这种情况下我们只有每个方法的一个版本,子类可以访问父类方法,所以我们可以直接调用父类的方法而不使用super

class Parentclass
{
   void display(){
	System.out.println("Parent class method");
   }
}
class Subclass extends Parentclass
{
   void printMsg(){
	/* This would call method of parent class,
	 * no need to use super keyword because no other
	 * method with the same name is present in this class
	 */
	display();
   } 
   public static void main(String args[]){

	Subclass obj= new Subclass();
        obj.printMsg(); 
   }
}

输出:

Parent class method

Java 中的方法重载

原文: https://beginnersbook.com/2013/05/method-overloading/

方法重载是一种功能,如果类的参数列表不同,则允许类具有多个具有相同名称的方法。它类似于 Java 中的构造函数重载,它允许类具有多个具有不同参数列表的构造函数。

让我们回到这一点,当我说参数列表时它意味着方法具有的参数:例如,具有两个参数的方法add(int a, int b)的参数列表与方法add(int a, int b, int c)的参数列表不同,具有三个参数。

重载方法的三种方法

为了重载方法,方法的参数列表必须在以下任何一个方面有所不同:

  1. 参数数量。

例如:这是一个有效的重载情况

add(int, int)
add(int, int, int)
  1. 参数的数据类型。

例如:

add(int, int)
add(int, float)
  1. 参数的数据类型序列。

例如:

add(int, float)
add(float, int)

方法重载的情况无效:

当我说参数列表时,我不是在讨论方法的返回类型,例如,如果两个方法具有相同的名称,相同的参数并且具有不同的返回类型,那么这不是一个有效的方法重载示例。这将抛出编译错误。

int add(int, int)
float add(int, int)

方法重载静态多态的一个例子。我们将在单独的教程中讨论多态及其类型。

注意事项:

  1. 静态多态也称为编译时绑定或早期绑定。
  2. 静态绑定在编译时发生。方法重载是静态绑定的一个示例,其中方法调用与其定义的绑定在编译时发生。

方法重载示例

如本指南开头所述,方法重载是通过使用不同参数声明相同方法来完成的。参数在以下任何一个中都必须不同:参数(或参数)的数量,顺序或类型。让我们看看每个案例的例子。

参数列表也称为参数列表

示例 1:重载 - 参数列表中的参数数量不同

此示例显示了如何通过具有不同数量的参数来完成方法重载:

class DisplayOverloading
{
    public void disp(char c)
    {
         System.out.println(c);
    }
    public void disp(char c, int num)  
    {
         System.out.println(c + " "+num);
    }
}
class Sample
{
   public static void main(String args[])
   {
       DisplayOverloading obj = new DisplayOverloading();
       obj.disp('a');
       obj.disp('a',10);
   }
}

输出:

a
a 10

在上面的例子中 - 方法disp()根据参数的数量重载 - 我们有两个名为disp的方法,但它们的参数不同。两者都具有不同数量的参数。

示例 2:重载 - 参数的数据类型的差异

在此示例中,方法disp()基于参数的数据类型重载 - 我们有两个名为disp()的方法,一个使用char类型的参数,另一个方法使用int类型的参数。

class DisplayOverloading2
{
    public void disp(char c)
    {
        System.out.println(c);
    }
    public void disp(int c)
    {
       System.out.println(c );
    }
}

class Sample2
{
    public static void main(String args[])
    {
        DisplayOverloading2 obj = new DisplayOverloading2();
        obj.disp('a');
        obj.disp(5);
    }
}

输出:

a
5

示例 3:重载 - 参数的数据类型顺序

这里方法disp()根据参数的数据类型序列重载 - 这两种方法在参数列表中都有不同的数据类型序列。第一种方法是将参数列表作为(char, int),第二种方法是使用(int, char)。由于序列不同,该方法可以过载而没有任何问题。

class DisplayOverloading3
{
   public void disp(char c, int num)
   {
       System.out.println("I’m the first definition of method disp");
   }
   public void disp(int num, char c)
   {
       System.out.println("I’m the second definition of method disp" );
   }
}
class Sample3
{
   public static void main(String args[])
   {
       DisplayOverloading3 obj = new DisplayOverloading3();
       obj.disp('x', 51 );
       obj.disp(52, 'y');
   }
}

输出:

I’m the first definition of method disp
I’m the second definition of method disp

方法重载和类型提升

当较小尺寸的数据类型被提升为比这更大的数据类型时称为类型提升,例如:字节数据类型可以提升为short,短数据类型可以提升为intlongdouble等。

它与方法重载有什么关系?

嗯,理解类型提升是非常重要的,否则你会认为程序会抛出编译错误,但实际上程序会因为类型提升而运行正常。

让我们举一个例子,看看我在这里说的是什么:

class Demo{
   void disp(int a, double b){
	System.out.println("Method A");
   }
   void disp(int a, double b, double c){
	System.out.println("Method B");
   }
   public static void main(String args[]){
	Demo obj = new Demo();
	/* I am passing float value as a second argument but
	 * it got promoted to the type double, because there
	 * wasn't any method having arg list as (int, float)
	 */
	obj.disp(100, 20.67f);
   }
}

输出:

Method A

正如你可以看到我在调用disp()方法时传递了float值,但它被提升为double类型,因为没有任何带参数列表的方法为(int, float)

但是这种类型的促销并不总是发生,让我们看另一个例子:

class Demo{
   void disp(int a, double b){
	System.out.println("Method A");
   }
   void disp(int a, double b, double c){
	System.out.println("Method B");
   }
   void disp(int a, float b){
	System.out.println("Method C");
   }
   public static void main(String args[]){
	Demo obj = new Demo();
	/* This time promotion won't happen as there is
	 * a method with arg list as (int, float)
	 */
	obj.disp(100, 20.67f);
   }
}

输出:

Method C

如您所见,此时类型提升未发生,因为存在具有匹配参数类型的方法。

类型升级表:

左侧的数据类型可以升级为右侧的任何数据类型。

byte → short → int → long
short → int → long
int → long → float → double
float → double
long → float → double

让我们看几个有效/无效的方法重载案例

情况 1:

int mymethod(int a, int b, float c)
int mymethod(int var1, int var2, float var3)

结果:编译时间错误。参数列表完全相同。两种方法都具有相同的数字,数据类型和相同的数据类型序列。

案例 2:

int mymethod(int a, int b)
int mymethod(float var1, float var2)

结果:非常好。有效的重载情况。这里参数的数据类型是不同的。

案例 3:

int mymethod(int a, int b)
int mymethod(int num)

结果:非常好。有效的重载情况。这里的参数数量不同。

案例 4:

float mymethod(int a, float b)
float mymethod(float var1, int var2)

结果:非常好。有效的重载情况。参数的数据类型的顺序是不同的,第一种方法是(int, float),第二种方法是(float, int)

案例 5:

int mymethod(int a, int b)
float mymethod(int var1, int var2)

结果:编译时间错误。参数列表完全相同。尽管返回类型的方法不同,但它不是有效的情况。由于方法的返回类型在重载方法时无关紧要。

在程序结束前检查答案之前猜测答案:

问题 1 - 返回类型,方法名称和参数列表相同。

class Demo
{
   public int myMethod(int num1, int num2)
   { 
       System.out.println("First myMethod of class Demo");
       return num1+num2;
   }
   public int myMethod(int var1, int var2)
   {
       System.out.println("Second myMethod of class Demo");
       return var1-var2;
   }
}
class Sample4
{
   public static void main(String args[])
   {
       Demo obj1= new Demo();
       obj1.myMethod(10,10);
       obj1.myMethod(20,12);
   }
}

答案:

它会抛出一个编译错误:不能在同一个类中定义多个具有相同名称和参数列表的方法。

问题 2 - 返回类型不同。方法名称和参数列表相同。

class Demo2
{
   public double myMethod(int num1, int num2)
   {
      System.out.println("First myMethod of class Demo");
      return num1+num2;
   }
   public int myMethod(int var1, int var2)
   {
      System.out.println("Second myMethod of class Demo");
      return var1-var2;
   }
}
class Sample5
{
   public static void main(String args[])
   {
      Demo2 obj2= new Demo2();
      obj2.myMethod(10,10);
      obj2.myMethod(20,12);
   }
}

答案:

它会抛出一个编译错误:即使返回类型不同,也不能在类中给出多个具有相同名称和参数列表的方法。 方法返回类型在重载的情况下无关紧要。

java 中的方法覆盖

原文: https://beginnersbook.com/2014/01/method-overriding-in-java-with-example/

声明子类中已经存在于父类中的方法称为方法覆盖。完成覆盖,以便子类可以将自己的实现提供给父类已经提供的方法。在这种情况下,父类中的方法称为覆盖方法,子类中的方法称为覆盖方法。在本指南中,我们将看到 Java 中的方法覆盖是什么以及我们使用它的原因。

方法覆盖示例

让我们举一个简单的例子来理解这一点。我们有两个类:一个子类Boy和一个父类HumanBoy类扩展了Human类。这两个类都有一个共同的方法void eat()Boy类给eat()方法赋予了自己的实现,换句话说它覆盖了eat()方法。

方法覆盖的目的在这里很清楚。Child类想要给出自己的实现,这样当它调用这个方法时,就会打印出Boy正在吃而不是Human正在吃东西。

class Human{
   //Overridden method
   public void eat()
   {
      System.out.println("Human is eating");
   }
}
class Boy extends Human{
   //Overriding method
   public void eat(){
      System.out.println("Boy is eating");
   }
   public static void main( String args[]) {
      Boy obj = new Boy();
      //This will call the child class version of eat()
      obj.eat();
   }
}

输出:

Boy is eating

方法覆盖的优点

方法覆盖的主要优点是类可以为继承的方法提供自己的特定实现,甚至不需要修改父类代码

当一个类有多个子类时,这很有用,所以如果一个子类需要使用父类方法,它可以使用它,而其他想要具有不同实现的类可以使用覆盖功能进行更改而不触及父类码。

方法覆盖和动态方法调度

方法覆盖是运行时多态的示例。当父类引用指向子类对象时,则在运行时确定对覆盖方法的调用,因为在方法调用期间,要执行哪个方法(父类或子类)由对象类型确定。在运行时解析对覆盖方法的调用的过程称为动态方法调度。让我们看一个例子来理解这个:

class ABC{
   //Overridden method
   public void disp()
   {
	System.out.println("disp() method of parent class");
   }	   
}
class Demo extends ABC{
   //Overriding method
   public void disp(){
	System.out.println("disp() method of Child class");
   }
   public void newMethod(){
	System.out.println("new method of child class");
   }
   public static void main( String args[]) {
	/* When Parent class reference refers to the parent class object
	 * then in this case overridden method (the method of parent class)
	 *  is called.
	 */
	ABC obj = new ABC();
	obj.disp();

	/* When parent class reference refers to the child class object
	 * then the overriding method (method of child class) is called.
	 * This is called dynamic method dispatch and runtime polymorphism
	 */
	ABC obj2 = new Demo();
	obj2.disp();
   }
}

输出:

disp() method of parent class
disp() method of Child class

在上面的示例中,使用第二个对象(obj2)调用 disp()方法是运行时多态(或动态方法调度)。

注意:在动态方法调度中,对象可以调用子类的覆盖方法和基类的所有非覆盖方法,但是它不能调用子类中新声明的方法。在上面的例子中,对象obj2正在调用disp()。但是,如果您尝试使用obj2调用newMethod()方法(已在Demo类中新声明),那么您将使用以下消息给出编译错误:

Exception in thread "main" java.lang.Error: Unresolved compilation 
problem: The method xyz() is undefined for the type ABC

Java 中的方法覆盖规则

  1. 参数列表:覆盖方法的参数列表(子类的方法)必须与被覆盖方法(父类的方法)匹配。参数的数据类型及其序列应完全匹配。

  2. 覆盖方法(子类方法)的访问修饰符不能比父类的重写方法更具限制性。例如,如果父类方法的访问修饰符是public,则覆盖方法(子类方法)不能具有privateprotecteddefault访问修饰符,因为所有这三个访问修饰符都比public更具限制性。

    例如,这是不允许的,因为子类disp方法比基类(public)更具限制性(protected)。

    class MyBaseClass{
       public void disp()
       {
           System.out.println("Parent class method");
       }
    }
    class MyChildClass extends MyBaseClass{
       protected void disp(){
          System.out.println("Child class method");
       }
       public static void main( String args[]) {
          MyChildClass obj = new MyChildClass();
          obj.disp();
       }
    }
    

    输出:

    Exception in thread "main" java.lang.Error: Unresolved compilation 
    problem: Cannot reduce the visibility of the inherited method from MyBaseClass
    

    然而,这是完全有效的情况,因为公众的限制性比受保护的要少。相同的访问修饰符也是有效的。

    class MyBaseClass{
       protected void disp()
       {
           System.out.println("Parent class method");
       }
    }
    class MyChildClass extends MyBaseClass{
       public void disp(){
          System.out.println("Child class method");
       }
       public static void main( String args[]) {
          MyChildClass obj = new MyChildClass();
          obj.disp();
       }
    }
    

    输出:

    Child class method
    
  3. privatestaticfinal方法不能被覆盖,因为它们是类的本地方法。但是,静态方法可以在子类中重新声明,在这种情况下,子类方法的行为会有所不同,并且与父类的相同静态方法无关。

  4. 覆盖方法(子类的方法)可以抛出非受检的异常,无论被覆盖的方法(父类的方法)是否抛出任何异常。然而,最重要的方法不应该抛出检查的异常,这些异常是新的或比被覆盖方法声明的异常更宽。我们将在即将到来的教程中使用示例详细讨论这个问题。

  5. 覆盖方法的绑定在运行时发生,称为动态绑定

  6. 如果一个类正在扩展抽象类或实现接口,那么它必须覆盖所有抽象方法,除非该类本身是一个抽象类。

方法覆盖中的super关键字

super关键字用于调用父类方法/构造函数。 super.myMethod()调用基类的myMethod()方法,而super()调用基类的构造函数。让我们看一下在方法覆盖使用super

我们知道我们覆盖子类中的方法,然后使用子类对象调用该方法调用覆盖方法。通过使用super,我们可以调用覆盖方法,如下例所示:

class ABC{
   public void myMethod()
   {
	System.out.println("Overridden method");
   }	   
}
class Demo extends ABC{
   public void myMethod(){
	//This will call the myMethod() of parent class
	super.myMethod();
	System.out.println("Overriding method");
   }
   public static void main( String args[]) {
	Demo obj = new Demo();
	obj.myMethod();
   }
}

输出:

Class ABC: mymethod()
Class Test: mymethod()

如您所见,使用super关键字,我们可以访问覆盖的方法。

java 中方法重载和覆盖之间的区别

原文: https://beginnersbook.com/2014/01/difference-between-method-overloading-and-overriding-in-java/

在本教程中,我们将讨论 Java 中的重载和覆盖之间的区别。如果您不熟悉这些条款,请参阅以下帖子:

  1. java 中的方法重载
  2. javav 方法覆盖

在 Java 中重载与覆盖

  1. 重载发生在编译时,而重载发生在运行时:重载方法调用与其定义的绑定发生在编译时,但覆盖的方法调用绑定到其定义发生在运行。
  2. 静态方法可以重载,这意味着一个类可以有多个同名的静态方法。即使在子类中声明相同的静态方法,它与父类的相同方法无关,也无法覆盖静态方法。
  3. 最基本的区别是重载是在同一个类中完成的,而覆盖基类和子类是必需的。覆盖是关于为父类的继承方法提供特定实现。
  4. 静态绑定用于重载方法,动态绑定用于覆盖/覆盖方法。
  5. 性能:与覆盖相比,重载提供了更好的性能。原因是覆盖方法的绑定正在运行时完成。
  6. privatefinal方法可以重载但不能覆盖它们。这意味着一个类可以有多个具有相同名称的私有/最终方法,但子类不能覆盖其基类的私有/最终方法。
  7. 返回类型的方法在方法重载的情况下无关紧要,可以相同或不同。但是,在方法覆盖的情况下,覆盖方法可以具有更具体的返回类型(参考这里)。
  8. 在进行方法重载时,参数列表应该不同。方法重载中的参数列表应该相同。

重载示例

//A class for adding upto 5 numbers
class Sum
{
    int add(int n1, int n2) 
    {
        return n1+n2;
    }
    int add(int n1, int n2, int n3) 
    {
        return n1+n2+n3;
    }
    int add(int n1, int n2, int n3, int n4) 
    {
        return n1+n2+n3+n4;
    }
    int add(int n1, int n2, int n3, int n4, int n5) 
    {
        return n1+n2+n3+n4+n5;
    }
    public static void main(String args[])
    {
    	Sum obj = new Sum();
    	System.out.println("Sum of two numbers: "+obj.add(20, 21));
    	System.out.println("Sum of three numbers: "+obj.add(20, 21, 22));
    	System.out.println("Sum of four numbers: "+obj.add(20, 21, 22, 23));
    	System.out.println("Sum of five numbers: "+obj.add(20, 21, 22, 23, 24));
    }
}

输出:

Sum of two numbers: 41
Sum of three numbers: 63
Sum of four numbers: 86
Sum of five numbers: 110

这里我们有 4 个版本的相同方法add。我们在这里重载方法add()

覆盖示例

package beginnersbook.com;
class CarClass
{
    public int speedLimit() 
    {
        return 100;
    }
}
class Ford extends CarClass
{
    public int speedLimit()
    {
        return 150;
    }
    public static void main(String args[])
    {
    	CarClass obj = new Ford();
    	int num= obj.speedLimit();
    	System.out.println("Speed Limit is: "+num);
    }
}

输出:

Speed Limit is: 150

这里Ford类的speedLimit()方法覆盖了类CarClassspeedLimit()方法。

Java 中的多态

原文: https://beginnersbook.com/2013/03/polymorphism-in-java/

多态是 OOP 特征之一,它允许我们以不同的方式执行单个动作。例如,假设我们有一个Animal类,它有一个方法sound()。由于这是一个泛型类,所以我们不能给它一个实现,如:RoarMeowOink等。我们不得不给出一个通用的消息。

public class Animal{
   ...
   public void sound(){
      System.out.println("Animal is making a sound");   
   }
}

现在假设我们动物类的两个子类:HorseCat扩展(参见继承Animal类。我们可以提供相同的方法实现,如下所示:

public class Horse extends Animal{
...
    @Override
    public void sound(){
        System.out.println("Neigh");
    }
}

public class Cat extends Animal{
...
    @Override
    public void sound(){
        System.out.println("Meow");
    }
}

正如您所看到的那样,尽管我们对所有子类sound()都有共同的操作,但是有不同的方法来执行相同的操作。这是多态的完美示例(允许我们以不同方式执行单个操作的功能)。仅调用泛型sound()方法没有任何意义,因为每个Animal都有不同的声音。因此,我们可以说该方法执行的操作基于对象的类型。

什么是编程中的多态?

多态是一种方法根据它所作用的对象做不同事物的能力。换句话说,多态允许您定义一个接口并具有多个实现。正如我们在上面的例子中看到的那样,我们已经定义了方法sound()并且在不同的 2 个子类中具有它的多个实现。
将在运行时确定将调用哪个sound()方法,因此我们上面给出的示例是运行时多态示例

多态的类型和方法重载&覆盖在单独的教程中。你可以在这里引用它们:
1. Java 中的方法重载 - 这是编译时间(或静态多态)的一个例子
2. Java 中的方法覆盖 - 这是运行时间(或动态多态)的一个例子
3. 多态的类型 - 运行时和编译时间 - 这是我们的下一个教程,我们详细介绍了多态的类型。在推荐这个主题之前,我会建议你通过方法重载和覆盖。

让我们写下它的完整代码:

例 1:Java 中的多态

运行时多态示例:

Animal.java

public class Animal{
   public void sound(){
      System.out.println("Animal is making a sound");   
   }
}

Horse.java

class Horse extends Animal{
    @Override
    public void sound(){
        System.out.println("Neigh");
    }
    public static void main(String args[]){
    	Animal obj = new Horse();
    	obj.sound();
    }
}

输出:

Neigh

Cat.java

public class Cat extends Animal{
    @Override
    public void sound(){
        System.out.println("Meow");
    }
    public static void main(String args[]){
    	Animal obj = new Cat();
    	obj.sound();
    }
}

输出:

Meow

例 2:编译时多态

方法重载另一方面是编译时多态示例。

class Overload
{
    void demo (int a)
    {
       System.out.println ("a: " + a);
    }
    void demo (int a, int b)
    {
       System.out.println ("a and b: " + a + "," + b);
    }
    double demo(double a) {
       System.out.println("double a: " + a);
       return a*a;
    }
}
class MethodOverloading
{
    public static void main (String args [])
    {
        Overload Obj = new Overload();
        double result;
        Obj .demo(10);
        Obj .demo(10, 20);
        result = Obj .demo(5.5);
        System.out.println("O/P : " + result);
    }
}

这里方法demo()重载 3 次:第一种方法有 1 个int参数,第二种方法有 2 个int参数,第三种方法有double参数。要调用哪个方法取决于调用方法时传递的参数。这发生在编译时而不是运行时,因此这种类型的多态称为编译时多态。

输出:

a: 10
a and b: 10,20
double a: 5.5
O/P : 30.25

java 的多态类型 - 运行时和编译时多态

原文: https://beginnersbook.com/2013/04/runtime-compile-time-polymorphism/

在上一篇教程中,我们讨论了 Java 中的[多态。在本指南中,我们将看到类型的多态。 java 中有两种类型的多态:

1)静态多态又称编译时多态

2)动态多态又称运行时多态](https://beginnersbook.com/2013/03/polymorphism-in-java/)

编译时多态(或静态多态)

在编译器时间内解析的多态称为静态多态。方法重载是编译时多态的一个例子。
方法重载:如果方法的参数在参数的数量,顺序和数据类型上不同,这允许我们有多个具有相同名称的方法。我们在这里已经讨论了方法重载:如果你没有阅读该指南,请参考: Java 中的方法重载

静态多态的例子

方法重载是 java 支持静态多态的方式之一。这里我们有两个相同方法add()的定义,其中add方法将在编译时由参数列表确定。这就是为什么它也被称为编译时多态。

class SimpleCalculator
{
    int add(int a, int b)
    {
         return a+b;
    }
    int  add(int a, int b, int c)  
    {
         return a+b+c;
    }
}
public class Demo
{
   public static void main(String args[])
   {
	   SimpleCalculator obj = new SimpleCalculator();
       System.out.println(obj.add(10, 20));
       System.out.println(obj.add(10, 20, 30));
   }
}

输出:

30
60

运行时多态(或动态多态)

它也称为动态方法调度。动态多态是一个在运行时解析对重写方法的调用的过程,这就是为什么它被称为运行时多态。我已经在单独的教程中详细讨论了方法覆盖,请参考:方法覆盖 Java

示例

在此示例中,我们有两个类ABCXYZABC是父类,XYZ是子类。子类覆盖父类的myMethod()方法。在此示例中,我们将子类对象分配给父类引用,因此为了确定将调用哪个方法,将在运行时确定对象的类型。对象的类型决定了要调用哪个版本的方法(而不是引用的类型)。

要理解覆盖的概念,您应该具有 Java 中继承的基本知识

class ABC{
   public void myMethod(){
	System.out.println("Overridden Method");
   }
}
public class XYZ extends ABC{

   public void myMethod(){
	System.out.println("Overriding Method");
   }
   public static void main(String args[]){
	ABC obj = new XYZ();
	obj.myMethod();
   }
}

输出:

Overriding Method

当通过父类的引用调用重写方法时,该对象的类型确定要执行哪个方法。因此,该确定在运行时进行。
由于两个类,子类和父类具有相同的方法animalSound。将在运行时由 JVM 确定将调用哪个版本的方法(子类或父类)。

几个最重要的例子:

ABC obj = new ABC();
obj.myMethod();
// This would call the myMethod() of parent class ABC

XYZ obj = new XYZ();
obj.myMethod();
// This would call the myMethod() of child class XYZ

ABC obj = new XYZ();
obj.myMethod();
// This would call the myMethod() of child class XYZ

在第三种情况下,要执行子类的方法,因为要执行的方法由对象的类型决定,并且由于对象属于子类,因此调用myMethod()的子类版本。

java 中的静态和动态绑定

原文: https://beginnersbook.com/2013/04/java-static-dynamic-binding/

方法调用与方法体的关联称为绑定。有两种类型的绑定:静态绑定在编译时发生,动态绑定在运行时发生。在我解释 java 中的静态和动态绑定之前,让我们看一些可以帮助您更好地理解这个概念的术语。

什么是引用和对象?

class Human{
....
}
class Boy extends Human{
   public static void main( String args[]) {
       /*This statement simply creates an object of class
        *Boy and assigns a reference of Boy to it*/  
       Boy obj1 = new Boy();

       /* Since Boy extends Human class. The object creation
        * can be done in this way. Parent class reference 
        * can have child class reference assigned to it
        */
       Human obj2 = new Boy();
   }
}

Java 中的静态和动态绑定

如上所述,方法定义与方法调用的关联称为绑定。有两种类型的绑定:静态绑定和动态绑定。让我们讨论一下。

静态绑定或早期绑定

编译器在编译时可以解析的绑定称为静态或早期绑定。staticprivatefinal方法的绑定是编译时为什么? 原因是无法覆盖这些方法,并且在编译时确定类的类型。让我们看一个例子来理解这个:

静态绑定示例

在这里,我们有两个类人类和男孩。这两个类都有相同的方法walk(),但方法是静态的,这意味着它不能被覆盖,所以即使我在创建对象obj时使用了Boy类的对象,它也会调用父类方法。因为引用是Human类型(父类)。因此,每当静态,私有和最终方法的绑定发生时,类的类型由编译器在编译时确定,然后绑定发生在那里。

class Human{
   public static void walk()
   {
       System.out.println("Human walks");
   }
}
class Boy extends Human{
   public static void walk(){
       System.out.println("Boy walks");
   }
   public static void main( String args[]) {
       /* Reference is of Human type and object is
        * Boy type
        */
       Human obj = new Boy();
       /* Reference is of HUman type and object is
        * of Human type.
        */
       Human obj2 = new Human();
       obj.walk();
       obj2.walk();
   }
}

输出:

Human walks
Human walks

动态绑定或后期绑定

当编译器无法在编译时解析调用/绑定时,此类绑定称为动态绑定或后期绑定。方法覆盖是动态绑定的一个完美示例,因为覆盖父类和子类具有相同的方法,在这种情况下,对象的类型确定要执行的方法。对象的类型在运行时确定,因此称为动态绑定

动态绑定示例

这是我们在上面看到的相同的例子。这里唯一的区别是,在这个例子中,覆盖实际上正在发生,因为这些方法是而不是静态,私有和最终。在覆盖的情况下,对覆盖方法的调用在运行时由对象类型确定,从而发生后期绑定。让我们看一个例子来理解这个:

class Human{
   //Overridden Method
   public void walk()
   {
       System.out.println("Human walks");
   }
}
class Demo extends Human{
   //Overriding Method
   public void walk(){
       System.out.println("Boy walks");
   }
   public static void main( String args[]) {
       /* Reference is of Human type and object is
        * Boy type
        */
       Human obj = new Demo();
       /* Reference is of HUman type and object is
        * of Human type.
        */
       Human obj2 = new Human();
       obj.walk();
       obj2.walk();
   }
}

输出:

Boy walks
Human walks

正如您所看到的那样,输出与我们在静态绑定示例中看到的不同,因为在这种情况下,在创建对象obj时,对象的类型被确定为Boy类型,因此调用Boy类的方法。请记住,对象的类型是在运行时确定的。

静态绑定与动态绑定

让我们讨论 Java 中静态和动态绑定之间的差异

  1. 静态绑定在编译时发生,而动态绑定在运行时发生。
  2. 私有,静态和最终方法的绑定总是在编译时发生,因为这些方法无法被覆盖。当实际发生方法覆盖并将父类型的引用分配给子类类型的对象时,则在运行时期间解析此类绑定。
  3. 重载方法的绑定是静态的,并且覆盖方法的绑定是动态的。

Java 编程简介

原文: https://beginnersbook.com/2013/05/java-introduction/

JAVA 由 Sun Microsystems Inc 于 1991 年开发,后来被 Oracle Corporation 收购。它由 James Gosling 和 Patrick Naughton 开发。它是一种简单的编程语言。在 java 中编写,编译和调试程序很容易。它有助于创建模块化程序和可重用代码。

Java 术语

在我们开始学习 Java 之前,让我们熟悉常见的 Java 术语。

Java 虚拟机(JVM)

这通常称为 JVM。之前,我们讨论 JVM 让我们看看程序执行的各个阶段。阶段如下:我们编写程序,然后编译程序,最后运行程序。

1)程序的编写当然是由像你我这样的 java 程序员完成的。

2)程序编译由 javac 编译器完成,javac 是 java 开发工具包(JDK)中包含的主 java 编译器。它将 java 程序作为输入并生成 java 字节码作为输出。

3)在第三阶段,JVM 执行编译器生成的字节码。这称为程序运行阶段。

所以,现在我们知道 JVM 的主要功能是执行编译器生成的字节码。 每个操作系统都有不同的 JVM,但是在执行字节码后它们产生的输出在所有操作系统中是相同的。这就是我们将 java 称为平台无关语言的原因。

字节码

如上所述,JDK 的javac编译器将 java 源代码编译成字节码,以便它可以由 JVM 执行。字节码由编译器保存在.class文件中。

Java 开发工具包(JDK)

在解释 JVM 和字节码时,我使用了术语 JDK。我们来讨论一下。顾名思义,这是完整的 Java 开发工具包,包括 JRE(Java 运行时环境),编译器和各种工具,如 JavaDoc,Java 调试器等。
为了创建,编译和运行 Java 程序,您需要在您的上安装 JDK 电脑。

Java 运行时环境(JRE)

JRE 是 JDK 的一部分,这意味着 JDK 包含 JRE。如果在系统上安装了 JRE,则可以运行 java 程序,但是无法编译它。 JRE 包括 JVM,浏览器插件和 applet 支持。当您只需要在计算机上运行 java 程序时,您只需要 JRE。

这些是在 java 中混淆​​初学者的基本 java 术语。有关完整的 java 词汇表,请参阅此链接:
https://docs.oracle.com/javase/tutorial/information/glossary.html

JAVA 的主要特点

Java 是一种独立于平台的语言

编译器(javac)将源代码(.java文件)转换为字节代码(.class文件)。如上所述,JVM 执行编译器生成的字节码。这个字节代码可以在任何平台上运行,例如 Windows,Linux,Mac OS 等。这意味着在 Windows 上编译的程序可以在 Linux 上运行,反之亦然。每个操作系统都有不同的 JVM,但是在执行字节码后它们产生的输出在所有操作系统中都是相同的。这就是我们将 java 称为平台无关语言的原因。

Java 是面向对象的语言

面向对象编程是一种将程序组织为对象集合的方法,每个对象都代表一个类的实例。

面向对象编程的 4 个主要概念是:

  1. 抽象
  2. 封装
  3. 继承
  4. 多态

简单

Java 被认为是一种简单的语言,因为它没有像运算符重载,多重继承,指针和显式内存分配这样的复杂功能。

健壮的语言

稳健意味着可靠。 Java 编程语言的开发方式非常强调早期检查可能的错误,这就是为什么 java 编译器能够检测其他编程语言中不易检测的错误的原因。使其健壮的 java 的主要特征是垃圾收集,异常处理和内存分配。

安全

我们没有指针,我们无法在 java 中访问超出绑定的数组(如果你试图这样做,你会得到ArrayIndexOutOfBoundsException)。这就是为什么在 Java 中无法利用堆栈损坏或缓冲区溢出等几个安全漏洞的原因。

Java 是分布式的

使用 java 编程语言我们可以创建分布式应用 RMI(远程方法调用)和 EJB(Enterprise Java Bean)用于在 java 中创建分布式应用。简单来说:java 程序可以分布在多个使用互联网连接的系统上。一个 JVM(java 虚拟机)上的对象可以在远程 JVM 上执行过程。

多线程

Java 支持多线程。多线程是一种 Java 功能,允许并发执行程序的两个或多个部分,以最大限度地利用 CPU。

便携

如上所述,在一台机器上编写的 java 代码可以在另一台机器上运行。平台无关的字节代码可以被携带到任何平台以便执行,从而使 java 代码可移植。

Java 中的抽象类

原文: https://beginnersbook.com/2013/05/java-abstract-class-method/

使用abstract关键字声明的类称为抽象类。它可以有抽象方法(没有主体的方法)以及具体方法(常规方法与主体)。普通类(非抽象类)不能有抽象方法。在本指南中,我们将了解什么是抽象类,我们使用它的原因以及在 Java 中使用它时必须记住的规则。

抽象类不能实例化,这意味着你不能创建它的对象。为什么?我们将在本指南的后面部分讨论。

为什么我们需要一个抽象类?

假设我们有一个类Animal有一个方法sound()和它的子类(参见继承),如DogLionHorseCat等。动物的声音因动物而异,没有必要在父类中实施这种方法。这是因为每个子类都必须覆盖此方法以给出自己的实现细节,例如Lion类将在此方法中说"Roar"Dog类将说"Woof"

因此,当我们知道所有动物子类将会并且应该重写此方法时,那么在父类中实现此方法是没有意义的。因此,使这个方法抽象是一个很好的选择,因为通过使这个方法抽象我们强制所有子类实现这个方法(否则你会得到编译错误),我们也不需要在父类中给这个方法任何实现。

由于Animal类有一个抽象方法,你必须要声明这个类是抽象的。

现在每个动物都必须有声音,通过使这个方法抽象化,我们强制要求子类为这个方法提供实现细节。这样我们就可以确保每只动物都有声音。

抽象类示例

//abstract parent class
abstract class Animal{
   //abstract method
   public abstract void sound();
}
//Dog class extends Animal class
public class Dog extends Animal{

   public void sound(){
	System.out.println("Woof");
   }
   public static void main(String args[]){
	Animal obj = new Dog();
	obj.sound();
   }
}

输出:

Woof

因此,对于这种情况,我们通常将类声明为抽象类,然后具体类扩展这些类并相应地重写方法,并且也可以拥有自己的方法。

抽象类声明

抽象类概述了方法,但未必实现所有方法。

//Declaration using abstract keyword
abstract class A{
   //This is abstract method
   abstract void myMethod();

   //This is concrete method with body
   void anotherMethod(){
      //Does something
   }
}

规则

注 1:正如我们在上面的例子中所看到的,有些情况下很难或经常不必在父类中实现所有方法。在这些情况下,我们可以将父类声明为abstract,这使得它成为一个特殊的类,它本身并不完整。

从抽象类派生的类必须实现在父类中声明为抽象的所有方法。

注 2:抽象类无法实例化,这意味着你无法创建它的对象。要使用此类,您需要创建另一个扩展此类的类并提供抽象方法的实现,然后您可以使用该子类的对象来调用父类的非抽象方法以及实现的方法(那些在父类中是抽象的,但在子类中实现)。

注 3:如果一个孩子没有实现抽象父类的所有抽象方法,那么子类也必须被声明为abstract

你知道吗? 由于抽象类也允许具体方法,因此它不提供 100%抽象。你可以说它提供了部分抽象。抽象是一个过程,您只显示“相关”数据并从用户“隐藏”对象的不必要细节。

另一方面,接口用于 100%抽象(这里有关抽象的更多信息)。
您可能还想阅读: Java 中抽象类和接口之间的区别

为什么我们不能创建抽象类的对象?

因为这些类是不完整的,所以它们具有没有主体的抽象方法,所以如果 java 允许你创建这个类的对象,那么如果有人使用该对象调用抽象方法那么会发生什么?没有实际的方法实现调用。
也因为一个对象是具体的。抽象类就像一个模板,所以你必须先扩展它并在它之前构建它。

用于演示不允许创建抽象类的对象的示例

如上所述,我们无法实例化抽象类。该程序抛出编译错误。

abstract class AbstractDemo{
   public void myMethod(){
      System.out.println("Hello");
   }
   abstract public void anotherMethod();
}
public class Demo extends AbstractDemo{

   public void anotherMethod() { 
        System.out.print("Abstract method"); 
   }
   public static void main(String args[])
   { 
      //error: You can't create object of it
      AbstractDemo obj = new AbstractDemo();
      obj.anotherMethod();
   }
}

输出:

Unresolved compilation problem: Cannot instantiate the type AbstractDemo

注意:扩展抽象类的类必须实现它的所有抽象方法,否则你也必须声明该类抽象。

抽象类 vs 具体类

非抽象的类称为 具体类。在我们在本指南开头看到的上述例子中,Animal是一个抽象类,CatDogLion是具体的课程。

要点:

  1. 除非被其他类扩展,否则抽象类没有用处。
  2. 如果在类中声明抽象方法,那么您也必须声明类抽象。你不能在具体的类中使用抽象方法。反之亦然并非总是如此:如果一个类没有任何抽象方法,那么它也可以被标记为抽象。
  3. 它也可以有非抽象方法(具体)。

我已经在单独的教程中介绍了抽象方法的规则和示例,你可以在这里找到指南: Java 中的抽象方法
现在让我们看一些基本知识和抽象方法的例子。

1)抽象方法没有正文。

2)始终以分号;)结束声明。

3)必须覆盖。必须扩展抽象类,并且必须以相同的方式覆盖抽象方法。

4)必须将类声明为抽象的抽象方法。

注意:扩展抽象类的类必须覆盖所有抽象方法。

抽象类和方法的示例

abstract class MyClass{
   public void disp(){
     System.out.println("Concrete method of parent class");
   }
   abstract public void disp2();
}

class Demo extends MyClass{
   /* Must Override this method while extending
    * MyClas
    */
   public void disp2()
   {
       System.out.println("overriding abstract method");
   }
   public static void main(String args[]){
       Demo obj = new Demo();
       obj.disp2();
   }
}

输出:

overriding abstract method

Java 中的抽象方法

原文: https://beginnersbook.com/2014/01/abstract-method-with-examples-in-java/

没有主体的方法(没有实现)被称为抽象方法。方法必须始终在抽象类中声明,或者换句话说,如果类具有抽象方法,则应该将其声明为抽象方法。在上一个教程中我们讨论了抽象类,如果你还没有检查过它,请在这里阅读它: Java 中的抽象类,在阅读本指南之前。
这是一个抽象方法在 java 中的外观:

public abstract int myMethod(int n1, int n2);

如你所见,这没有身体。

抽象方法规则

  1. 抽象方法没有正文,它们只有如上所示的方法签名。
  2. 如果一个类有一个抽象方法,它应该被声明为abstract,反之亦然,这意味着一个抽象类不需要一个抽象方法是强制性的。
  3. 如果常规类扩展了一个抽象类,那么该类必须实现抽象父类的所有抽象方法,或者它也必须被声明为abstract

示例 1:抽象类中的抽象方法

//abstract class
abstract class Sum{
   /* These two are abstract methods, the child class
    * must implement these methods
    */
   public abstract int sumOfTwo(int n1, int n2);
   public abstract int sumOfThree(int n1, int n2, int n3);

   //Regular method 
   public void disp(){
	System.out.println("Method of class Sum");
   }
}
//Regular class extends abstract class
class Demo extends Sum{

   /* If I don't provide the implementation of these two methods, the
    * program will throw compilation error.
    */
   public int sumOfTwo(int num1, int num2){
	return num1+num2;
   }
   public int sumOfThree(int num1, int num2, int num3){
	return num1+num2+num3;
   }
   public static void main(String args[]){
	Sum obj = new Demo();
	System.out.println(obj.sumOfTwo(3, 7));
	System.out.println(obj.sumOfThree(4, 3, 19));
	obj.disp();
   }
}

输出:

10
26
Method of class Sum

例 2:接口中的抽象方法

默认情况下,接口的所有方法都是公共抽象的。您不能在接口中使用具体的(常规方法和正文)方法。

//Interface
interface Multiply{
   //abstract methods
   public abstract int multiplyTwo(int n1, int n2);

   /* We need not to mention public and abstract in interface
    * as all the methods in interface are 
    * public and abstract by default so the compiler will
    * treat this as 
    * public abstract multiplyThree(int n1, int n2, int n3);
    */
   int multiplyThree(int n1, int n2, int n3);

   /* Regular (or concrete) methods are not allowed in an interface
    * so if I uncomment this method, you will get compilation error
    * public void disp(){
    *    System.out.println("I will give error if u uncomment me");
    * }
    */
}

class Demo implements Multiply{
   public int multiplyTwo(int num1, int num2){
      return num1*num2;
   }
   public int multiplyThree(int num1, int num2, int num3){
      return num1*num2*num3;
   }
   public static void main(String args[]){
      Multiply obj = new Demo();
      System.out.println(obj.multiplyTwo(3, 7));
      System.out.println(obj.multiplyThree(1, 9, 0));
   }
}

输出:

21
0

参考:

抽象方法 javadoc

java 中的接口

原文: https://beginnersbook.com/2013/05/java-interface/

在上一个教程中,我们讨论了抽象类,它用于实现部分抽象。与抽象类不同,接口用于完全抽象。抽象是一个过程,您只显示“相关”数据并“隐藏”用户不必要的对象细节(参见:抽象)。在本指南中,我们将介绍 java 中的接口,我们使用它的原因以及在 Java 编程中使用接口时必须遵循的规则。

Java 中的接口是什么?

接口看起来像一个类,但它不是一个类。接口可以像类一样拥有方法和变量,但接口中声明的方法默认是抽象的(只有方法签名,没有正文,请参阅: Java 抽象方法)。此外,在接口中声明的变量是public static最后默认。我们将在本指南后面详细介绍。

Java 中的接口有什么用?

如上所述,它们用于完全抽象。由于接口中的方法没有正文,因此必须先由类实现它们才能访问它们。实现接口的类必须实现该接口的所有方法。此外,java 编程语言不允许扩展多个类,但是您可以在类中实现多个接口。

语法:

通过指定关键字interface声明接口。例如:

interface MyInterface
{
   /* All the methods are public abstract by default
    * As you see they have no body
    */
   public void method1();
   public void method2();
}

Java 中的接口示例

这是类实现接口的方式。它必须提供在接口中声明的所有方法的主体,或者换句话说,您可以说该类必须实现接口的所有方法。

你知道吗?implements接口但是接口extends的另一个接口。

interface MyInterface
{
   /* compiler will treat them as: 
    * public abstract void method1();
    * public abstract void method2();
    */
   public void method1();
   public void method2();
}
class Demo implements MyInterface
{
   /* This class must have to implement both the abstract methods
    * else you will get compilation error
    */
   public void method1()
   {
	System.out.println("implementation of method1");
   }
   public void method2()
   {
	System.out.println("implementation of method2");
   }
   public static void main(String arg[])
   {
	MyInterface obj = new Demo();
	obj.method1();
   }
}

输出:

implementation of method1

您可能还想阅读: 抽象类和接口之间的区别

接口和继承

如上所述,接口不能实现另一个接口。它必须扩展其他接口。请参阅下面的示例,其中我们有两个接口Inf1Inf2Inf2扩展了Inf1所以如果类实现了Inf2,它必须提供Inf2Inf1接口的所有方法的实现。

在此处了解有关继承的更多信息: Java 继承

interface Inf1{
   public void method1();
}
interface Inf2 extends Inf1 {
   public void method2();
}
public class Demo implements Inf2{
   /* Even though this class is only implementing the
    * interface Inf2, it has to implement all the methods 
    * of Inf1 as well because the interface Inf2 extends Inf1
    */
    public void method1(){
	System.out.println("method1");
    }
    public void method2(){
	System.out.println("method2");
    }
    public static void main(String args[]){
	Inf2 obj = new Demo();
	obj.method2();
    }
}

在这个程序中,类Demo只实现接口Inf2,但是它必须提供接口Inf1的所有方法的实现,因为接口Inf2扩展了Inf1

Java 中的标记或标记接口

空接口称为标记或标记接口。例如,SerializableEventListenerRemotejava.rmi.Remote)是标记接口。这些接口中没有任何字段和方法。在这里阅读更多相关信息

嵌套接口

在另一个接口或类中声明的接口称为嵌套接口。它们也被称为内部接口。例如,集合框架中的Entry接口在Map接口内声明,这就是我们不直接使用它的原因,而是我们这样使用它:Map.Entry

要点:以下是关于接口的关键要点:

1)我们无法在 java 中实例化接口。这意味着我们无法创建接口的对象

2)接口提供完全抽象,因为它的方法都没有。另一方面,抽象类提供了部分抽象,因为它可以具有抽象和具体(带有正文的方法)方法。

3)类使用implements关键字来实现接口。

4)虽然在接口的任何方法的类中提供实现,但它需要被公开提及。

5)实现任何接口的类必须实现该接口的所有方法,否则应该将该类声明为abstract

6)接口不能声明为私有,受保护或瞬态。

7)所有接口方法默认为abstractpublic

8)接口中声明的变量默认为publicstaticfinal

interface Try
{
   int a=10;
   public int a=10;
   public static final int a=10;
   final int a=10;
   static int a=0;
}

以上所有陈述都是相同的。

9)接口变量必须在声明时初始化,否则编译器将抛出错误。

interface Try
{
      int x;//Compile-time error
}

上面的代码将抛出编译时错误,因为变量x的值在声明时未初始化。

10)在任何实现类中,您都不能更改在interface中声明的变量,因为默认情况下它们是publicstaticfinal。这里我们实现了具有变量x的接口Try。当我们尝试设置变量x的值时,我们得到了编译错误,因为变量x默认是公共静态final,并且最终变量无法重新初始化。

class Sample implements Try
{
  public static void main(String args[])
  {
     x=20; //compile time error
  }
}

11)接口可以扩展任何接口但不能实现它。类实现接口和接口扩展接口。

12)可以实现任何数量的接口

13)如果两个接口中存在两个或多个相同的方法并且一个类实现两个接口,则该方法的实现一次就足够了。

interface A
{
   public void aaa();
}
interface B
{
   public void aaa();
}
class Central implements A,B
{
   public void aaa()
   {
        //Any Code here
   }
   public static void main(String args[])
   {
        //Statements
    }
}

14)类不能实现两个具有相同名称但返回类型不同的方法的接口。

interface A
{
   public void aaa();
}
interface B
{
   public int aaa();
}

class Central implements A,B
{

   public void aaa() // error
   {
   }
   public int aaa() // error
   {
   }
   public static void main(String args[])
   {

   }
}

15)变量名称冲突可以通过接口名称解决。

interface A
{
    int x=10;
}
interface B
{
    int x=100;
}
class Hello implements A,B
{
    public static void Main(String args[])
    {
       /* reference to x is ambiguous both variables are x
        * so we are using interface name to resolve the 
        * variable
        */
       System.out.println(x); 
       System.out.println(A.x);
       System.out.println(B.x);
    }
}

java 中接口的优点:

使用接口的优点如下:

  1. 在不打扰实现部分的情况下,我们可以实现实现的安全性
  2. 在 java 中,不允许多重继承 ,但是您可以使用接口来使用它,因为您可以实现多个接口。

Java 中抽象类和接口的区别

原文: https://beginnersbook.com/2013/05/abstract-class-vs-interface-in-java/

在本文中,我们将通过示例讨论 Java 中抽象类和接口之间的**差异。我已经在 OOPs 概念 的单独教程中介绍了抽象类和接口,因此我建议您先阅读它们,然后再考虑差异。

  1. java 中的抽象类
  2. Java 中的接口**
抽象类 接口
1 抽象类一次只能扩展一个类或一个抽象类 接口一次可以扩展任意数量的接口
2 抽象类可以扩展另一个具体(常规)类或抽象类 接口只能扩展另一个接口
3 抽象类可以同时具有抽象和具体方法 接口只能有抽象方法
4 在抽象类关键字中,abstract是将方法声明为抽象的必需项 在接口关键字中,abstract是可选的,用于将方法声明为抽象
抽象类可以具有受保护和公共抽象方法 接口只能有公共抽象方法
6 抽象类可以使用任何访问描述符具有静态,最终或静态最终变量 接口只能有public static final(常量)变量

以下示例解释了上述每个要点:

Java 中的抽象类 vs 接口

差异 No.1:抽象类一次只能扩展一个类或一个抽象类

class Example1{
   public void display1(){
      System.out.println("display1 method");
   }
}
abstract class Example2{
   public void display2(){
      System.out.println("display2 method");
   }
}
abstract class Example3 extends Example1{
   abstract void display3();
}
class Example4 extends Example3{
   public void display3(){
      System.out.println("display3 method");
   }
}
class Demo{
   public static void main(String args[]){
       Example4 obj=new Example4();
       obj.display3();
   }
}

输出:

display3 method

接口可以一次扩展任意数量的接口

//first interface
interface Example1{
    public void display1();
}
//second interface
interface Example2 {
    public void display2();
}
//This interface is extending both the above interfaces
interface Example3 extends Example1,Example2{
}
class Example4 implements Example3{
    public void display1(){
        System.out.println("display2 method");
    }
    public void display2(){
        System.out.println("display3 method");
    }
}
class Demo{
    public static void main(String args[]){
        Example4 obj=new Example4();
        obj.display1();
    }
}

输出:

display2 method

差异 2:抽象类可以由类或抽象类扩展(继承)

class Example1{
   public void display1(){
      System.out.println("display1 method");
   }
}
abstract class Example2{
   public void display2(){
       System.out.println("display2 method");
   }
}
abstract class Example3 extends Example2{
   abstract void display3();
}
class Example4 extends Example3{
   public void display2(){
       System.out.println("Example4-display2 method");
   }
   public void display3(){
       System.out.println("display3 method");
   }
}
class Demo{
   public static void main(String args[]){
       Example4 obj=new Example4();
       obj.display2();
   }
}

输出:

Example4-display2 method

接口只能通过接口扩展。类必须实现它们而不是扩展

interface Example1{
    public void display1();
}
interface Example2 extends Example1{
}
class Example3 implements Example2{
   public void display1(){
      System.out.println("display1 method");
   }
}
class Demo{
   public static void main(String args[]){
      Example3 obj=new Example3();
      obj.display1();
   }
}

输出:

display1 method

差异 3:抽象类可以同时具有抽象和具体方法

abstract class Example1 {
   abstract void display1();
   public void display2(){
     System.out.println("display2 method");
   }
}
class Example2 extends Example1{
   public void display1(){
      System.out.println("display1 method");
   }
}
class Demo{
   public static void main(String args[]){
     Example2 obj=new Example2();
     obj.display1();
   }
}

接口只能有抽象方法,它们不能有具体方法

interface Example1{
   public abstract void display1();
}
class Example2 implements Example1{
   public void display1(){
      System.out.println("display1 method");
   }
}
class Demo{
   public static void main(String args[]){
      Example2 obj=new Example2();
      obj.display1();
   }
}

输出:

display1 method

差异 No.4:在抽象类中,关键字abstract是将方法声明为抽象的必需项

abstract class Example1{
   public abstract void display1();
}

class Example2 extends Example1{
   public void display1(){
      System.out.println("display1 method");
   }
   public void display2(){
      System.out.println("display2 method");
   }
}
class Demo{
   public static void main(String args[]){ 
       Example2 obj=new Example2(); 
       obj.display1();
   }
}

在接口中,关键字abstract是可选的,用于将方法声明为抽象,因为默认情况下所有方法都是抽象的

interface Example1{
    public void display1();
}
class Example2 implements Example1{
    public void display1(){
        System.out.println("display1 method");
    }
    public void display2(){
        System.out.println("display2 method");
    } 
}
class Demo{
   public static void main(String args[]){
       Example2 obj=new Example2();
       obj.display1();
   }
}

差异 No.5:抽象类可以有保护和公共抽象方法

abstract class Example1{
   protected abstract void display1();
   public abstract void display2();
   public abstract void display3();
}
class Example2 extends Example1{
   public void display1(){
       System.out.println("display1 method");
   }
   public void display2(){
      System.out.println("display2 method");
   }
   public void display3(){
      System.out.println("display3 method");
   }
}
class Demo{
   public static void main(String args[]){
      Example2 obj=new Example2();
      obj.display1();
   }
}

接口只能有公共抽象方法

interface Example1{
   void display1();
}
class Example2 implements Example1{
   public void display1(){
      System.out.println("display1 method");
   }
   public void display2(){ 
      System.out.println("display2 method");
   }
}
class Demo{
   public static void main(String args[]){
       Example2 obj=new Example2();
       obj.display1();
   }
}

差异 No.6:抽象类可以具有任何访问描述符的静态,最终或静态最终变量

abstract class Example1{
   private int numOne=10;
   protected final int numTwo=20;
   public static final int numThree=500;
   public void display1(){
      System.out.println("Num1="+numOne);
   }
}
class Example2 extends Example1{
   public void display2(){
      System.out.println("Num2="+numTwo);
      System.out.println("Num2="+numThree);
   }
}
class Demo{
   public static void main(String args[]){
      Example2 obj=new Example2(); 
      obj.display1();
      obj.display2();
   }
}

接口只能有公共静态最终(常量)变量

interface Example1{
   int numOne=10;
}
class Example2 implements Example1{
   public void display1(){
      System.out.println("Num1="+numOne);
   }
}
class Demo{
   public static void main(String args[]){
      Example2 obj=new Example2();
      obj.display1();
   }
}

Java 中的封装

原文: https://beginnersbook.com/2013/05/encapsulation-in-java/

封装只是意味着将对象状态(字段)和行为(方法)绑定在一起。如果您正在创建类,那么您正在进行封装。在本指南中,我们将看到如何在 java 程序中进行封装,如果您正在寻找封装的真实示例,请参考本指南:使用现实示例解释的 OOP 功能。

对于其他 OOP 主题,如继承多态,请参考 OOPs 概念

让我们回到主题。

什么是封装?

封装背后的整个想法是隐藏用户的实现细节。如果数据成员是私有的,则意味着它只能在同一个类中访问。没有外部类可以访问其他类的私有数据成员(变量)。

但是,如果我们设置公共获取器和设置器方法来更新(例如void setSSN(int ssn))和读取(例如int getSSN())私有数据字段,那么外部类可以通过公共方法访问这些私有数据字段。

这样,数据只能通过公共方法访问,从而使私有字段及其实现对外部类隐藏。这就是封装被称为数据隐藏的原因。 让我们看一个例子来更好地理解这个概念。

Java 中的封装示例

如何在 java 中实现封装:

1)将实例变量设为私有,以便不能直接从类外部访问它们。您只能通过类的方法设置和获取这些变量的值。

2)在类中使用获取器和设置器方法来设置和获取字段的值。

class EncapsulationDemo{
    private int ssn;
    private String empName;
    private int empAge;

    //Getter and Setter methods
    public int getEmpSSN(){
        return ssn;
    }

    public String getEmpName(){
        return empName;
    }

    public int getEmpAge(){
        return empAge;
    }

    public void setEmpAge(int newValue){
        empAge = newValue;
    }

    public void setEmpName(String newValue){
        empName = newValue;
    }

    public void setEmpSSN(int newValue){
        ssn = newValue;
    }
}
public class EncapsTest{
    public static void main(String args[]){
         EncapsulationDemo obj = new EncapsulationDemo();
         obj.setEmpName("Mario");
         obj.setEmpAge(32);
         obj.setEmpSSN(112233);
         System.out.println("Employee Name: " + obj.getEmpName());
         System.out.println("Employee SSN: " + obj.getEmpSSN());
         System.out.println("Employee Age: " + obj.getEmpAge());
    } 
}

输出:

Employee Name: Mario
Employee SSN: 112233
Employee Age: 32

在上面的示例中,所有三个数据成员(或数据字段)都是私有的(请参阅: Java 中的访问修饰符),无法直接访问。这些字段只能通过公共方法访问。字段empNamessnempAge使用 OOP 的封装技术制作隐藏数据字段。

封装的优点

  1. 它提高了可维护性和灵活性以及可重用性:例如,在上面的代码中,void setEmpName(String name)String getEmpName()的实现代码可以在任何时间点改变。由于实现纯粹是为外部类隐藏的,因此它们仍将使用相同的方法(setEmpName(String name)getEmpName())访问私有字段empName。因此,可以在任何时间点维护代码,而不会破坏使用代码的类。这提高了底层类的可重用性。
  2. 这些字段可以是只读的(如果我们没有在类中定义设置器方法)或只写(如果我们没有在类中定义获取器方法)。对于例如如果我们有一个我们不想改变的字段(或变量),那么我们只需将变量定义为private而不是设置器和获取器两者,我们只需要为该变量定义获取器方法。由于设置器方法不存在,外部类无法修改该字段的值。
  3. 用户不会知道幕后发生了什么。他们只知道要更新字段调用集方法并读取字段调用获取器方法,但这些设置器和获取器 方法正在做的事情纯粹是对它们隐藏的。

封装也称为“数据隐藏”。

java 中的包以及如何使用它们

原文: https://beginnersbook.com/2013/03/packages-in-java/

顾名思义,包是类,接口和其他包的包(组)。在 java 中,我们使用包来组织我们的类和接口。我们在 Java 中有两种类型的包:内置包和我们可以创建的包(也称为用户定义包)。在本指南中,我们将了解什么是包,什么是 java 中的用户定义包以及如何使用它们。

在 java 中我们有几个内置包,例如当我们需要用户输入时,我们导入一个这样的包:

import java.util.Scanner

这里:
java是顶级包
util是子包
Scanner是一个类是存在于子包util中。

在我们看到如何在 java 中创建用户定义的包之前,让我们看看使用包的优点。

在 Java 中使用包的优点

这些是您应该在 Java 中使用包的原因:

  • 可重用性:在 java 中开发项目时,我们常常觉得在我们的代码中我们一再写的东西很少。使用包,您可以在包内部以类的形式创建这样的东西,并且只要您需要执行相同的任务,只需导入该包并使用该类。
  • 更好的组织:同样,在我们有数百个类的大型 java 项目中,总是需要将相似类型的类分组到一个有意义的包名中,这样您就可以更好地组织项目了。需要一些东西,你可以快速找到它并使用它,这提高了效率。
  • 名称冲突:我们可以在不同的包中定义两个具有相同名称的类,以避免名称冲突,我们可以使用包

Java 中的包类型

正如本指南开头所提到的,我们在 java 中有两种类型的包。

1)用户定义的包:我们创建的包称为用户定义的包。

2)内置包:已经定义的包如java.io.*java.lang.*等被称为内置包。

我们已经讨论了内置包,可以借助示例讨论用户定义的包。

示例 1:Java 包

我在包名letmecalculate中创建了一个类Calculator。要在包中创建类,请在程序的第一个语句中声明包名。一个类只能有一个包声明。
在包letmecalculate内创建的Calculator.java文件

package letmecalculate;

public class Calculator {
   public int add(int a, int b){
	return a+b;
   }
   public static void main(String args[]){
	Calculator obj = new Calculator();
	System.out.println(obj.add(10, 20));
   }
}

现在让我们看看如何在另一个程序中使用此包。

import letmecalculate.Calculator;
public class Demo{
   public static void main(String args[]){
	Calculator obj = new Calculator();
	System.out.println(obj.add(100, 200));
   }
}

要使用类Calculator,我已导入包letmecalculate。在上面的程序中,我将包导入为letmecalculate.Calculator,这只导入了Calculator类。但是如果你在包letmecalculate中有几个类,那么你可以像这样导入包,以使用这个包的所有类。

import letmecalculate.*;

示例 2:在导入另一个包时在包内创建一个类

正如我们所看到的,包声明和包导入都应该是 java 程序中的第一个语句。让我们看看当我们在导入另一个包时在包内创建一个类时应该是什么顺序。

//Declaring a package
package anotherpackage;
//importing a package
import letmecalculate.Calculator;
public class Example{
   public static void main(String args[]){
	Calculator obj = new Calculator();
	System.out.println(obj.add(100, 200));
   }
}

所以这种情况下的顺序应该是:
→包的声明
→包的导入

示例 3:导入类时使用完全限定名称

您可以使用完全限定名称来避免import语句。让我们看一个例子来理解这个:

Calculator.java

package letmecalculate;
public class Calculator {
   public int add(int a, int b){
	return a+b;
   }
   public static void main(String args[]){
	Calculator obj = new Calculator();
	System.out.println(obj.add(10, 20));
   }
}

Example.java

//Declaring a package
package anotherpackage;
public class Example{
   public static void main(String args[]){
        //Using fully qualified name instead of import
	letmecalculate.Calculator obj = 
		new letmecalculate.Calculator();
	System.out.println(obj.add(100, 200));
   }
}

Example类中,我使用了诸如package_name.class_name之类的完全限定名来创建它的对象,而不是导入包。您可能还想阅读: Java 中的静态导入

Java 中的子包

另一个包中的包称为子包。例如,如果我在letmecalculate包中创建一个包,那么它将被称为子包。

假设我在letmecalculate中创建了另一个包,子包名称是multiply。所以如果我在这个子包中创建一个类,它应该在开头有这个包声明:

package letmecalculate.multiply;

Multiplication.java

package letmecalculate.multiply;
public class Multiplication {
	int product(int a, int b){
		return a*b;
	}
}

现在如果我需要使用这个Multiplication类,我必须像这样导入包:

import letmecalculate.multiply;

或者我可以使用这样的完全限定名称:

letmecalculate.multiply.Multiplication obj = 
     new letmecalculate.multiply.Multiplication();

要记住的要点:

  1. 有时可能会发生类名冲突。例如:假设我们有两个包abcpackagexyzpackage,两个包都有一个同名的类,让它为JavaExample.java。现在假设一个类导入这两个包,如下所示:
import abcpackage.*;
import xyzpackage.*;

这将抛出编译错误。要避免此类错误,您需要使用我上面显示的完全限定名称方法。例如

abcpackage.JavaExample obj = new abcpackage.JavaExample();
xyzpackage.JavaExample obj2 = new xyzpackage.JavaExample();

这样就可以避免导入包语句并避免名称冲突错误。

我已经在上面讨论了这个问题,让我在这里再说一遍。如果我们在导入另一个包时在包内创建一个类,那么包声明应该是第一个语句,然后是包导入。例如:

package abcpackage;
import xyzpackage.*;
  1. 一个类只能有一个包声明,但它可以有多个包导入语句。例如:
package abcpackage; //This should be one
import xyzpackage;
import anotherpackage;
import anything;
  1. 使用子包时,应谨慎使用*等通配符导入。例如:让我们说:我们有一个包abc,在那个包中我们有另一个包foo,现在foo是一个子包。

abc中的类是:Example1Example2Example3
foo中的类是:Demo1Demo2

所以,如果我使用这样的通配符导入包abc

import abc.*;

然后它只会导入类Example1Example2Example3,但它不会导入子包的类。

要导入子包的类,您需要像这样导入:

import abc.foo.*;

这将导入Demo1Demo2,但不会导入Example1Example2Example3

因此要导入包和子包中的所有类,我们需要使用两个import语句,如下所示:

import abc.*;
import abc.foo.*;

Java 访问修饰符 - 公共、私有、受保护和默认

原文: https://beginnersbook.com/2013/05/java-access-modifiers/

在练习 java 程序时,您必须已经看过公共,私有和受保护的关键字,这些被称为访问修饰符。访问修饰符限制对另一个类中的类,构造函数,数据成员和方法的访问。在 java 中我们有四个访问修饰符:

  1. 默认
  2. private
  3. protected
  4. public

1.默认访问修饰符

当我们没有提到任何访问修饰符时,它被称为默认访问修饰符。此修饰符的范围仅限于包。这意味着如果我们在包中有一个具有默认访问修饰符的类,则只有此包中的那些类才能访问此类。此程序包之外的其他任何类都无法访问此类。类似地,如果我们在类中有一个默认方法或数据成员,它将在另一个包的类中不可见。让我们看一个例子来理解这个:

Java 中的默认访问修饰符示例

要理解这个例子,你必须具备 java 中包的知识

在这个例子中我们有两个类,Test类试图访问Addition类的默认方法,因为类Test属于不同的包,这个程序会抛出编译错误,因为默认修饰符的范围仅限于同一个包中宣布它。

Addition.java

package abcpackage;

public class Addition {
   /* Since we didn't mention any access modifier here, it would
    * be considered as default.
    */
   int addTwoNumbers(int a, int b){
	return a+b;
   }
}

Test.java

package xyzpackage;

/* We are importing the abcpackage
 * but still we will get error because the
 * class we are trying to use has default access
 * modifier.
 */
import abcpackage.*;
public class Test {
   public static void main(String args[]){
	Addition obj = new Addition();
        /* It will throw error because we are trying to access
         * the default method in another package
         */
	obj.addTwoNumbers(10, 21);
   }
}

输出:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
The method addTwoNumbers(int, int) from the type Addition is not visible
at xyzpackage.Test.main(Test.java:12)

2.私有访问修饰符

private修饰符的范围仅限于类。

  1. 私有数据成员和方法只能在类中访问
  2. 类和接口不能声明为私有
  3. 如果某个类具有私有构造函数,那么您无法从该类外部创建该类的对象。

让我们看一个例子来理解这个:

java 中的私有访问修饰符示例

此示例抛出编译错误,因为我们尝试在类Example中访问类ABC的私有数据成员和方法。私有数据成员和方法只能在类中访问。

class ABC{  
   private double num = 100;
   private int square(int a){
	return a*a;
   }
}  
public class Example{
   public static void main(String args[]){  
	ABC obj = new ABC();  
	System.out.println(obj.num); 
	System.out.println(obj.square(10));
   }  
}

输出:

Compile - time error

3.受保护的访问修饰符

受保护的数据成员和方法只能由同一个包的类和任何包中的子类访问。您还可以说受保护的访问修饰符与默认访问修饰符类似,但有一个例外是它在子类中具有可见性。
类不能声明受保护。此访问修饰符通常用于父子关系。

Java 中的受保护访问修饰符示例

在此示例中,另一个包中存在的类Test能够调用addTwoNumbers()方法,该方法被声明为protected。这是因为Test类扩展了类Addition,而protected修饰符允许在子类(在任何包中)访问受保护的成员。

Addition.java

package abcpackage;
public class Addition {

   protected int addTwoNumbers(int a, int b){
	return a+b;
   }
}

Test.java

package xyzpackage;
import abcpackage.*;
class Test extends Addition{
   public static void main(String args[]){
	Test obj = new Test();
	System.out.println(obj.addTwoNumbers(11, 22));
   }
}

输出:

33

4.公共访问修饰符

可以从任何地方访问声明为public的成员,方法和类。此修饰符不对访问权限施加任何限制。

java 中的公共访问修饰符示例

让我们看一下上面看到的相同的例子,但这次方法addTwoNumbers()public修饰符,类Test能够访问这个方法,甚至不需要扩展Addition类。这是因为公共修饰符随处可见。

Addition.java

package abcpackage;

public class Addition {

   public int addTwoNumbers(int a, int b){
	return a+b;
   }
}

Test.java

package xyzpackage;
import abcpackage.*;
class Test{
   public static void main(String args[]){
      Addition obj = new Addition();
      System.out.println(obj.addTwoNumbers(100, 1));
   }
}

输出:

101

让我们以表格形式查看这些访问修饰符的范围:

表格形式的访问修饰符的范围

------------+-------+---------+--------------+--------------+--------
            | Class | Package | Subclass     | Subclass     |Outside|
            |       |         |(same package)|(diff package)|Class  |
————————————+———————+—————————+——————————----+—————————----—+————————
public      | Yes   |  Yes    |    Yes       |    Yes       |   Yes |    
————————————+———————+—————————+—————————----—+—————————----—+————————
protected   | Yes   |  Yes    |    Yes       |    Yes       |   No  |    
————————————+———————+—————————+————————----——+————————----——+————————
default     | Yes   |  Yes    |    Yes       |    No        |   No  |
————————————+———————+—————————+————————----——+————————----——+————————
private     | Yes   |  No     |    No        |    No        |   No  |
------------+-------+---------+--------------+--------------+--------

Java 中的垃圾收集

原文: https://beginnersbook.com/2013/04/java-garbage-collection/

当 JVM 启动时,它会创建一个堆区域,称为运行时数据区域。这是存储所有对象(类的实例)的地方。由于该区域有限,因此需要通过移除不再使用的对象来有效地管理该区域。从堆内存中删除未使用的对象的过程称为垃圾收集,这是 Java 中内存管理的一部分。

像 C/C++ 这样的语言没有支持自动垃圾收集,但是在 java 中,垃圾收集是自动的。

现在我们知道 java 中的垃圾收集是自动的。让我们看看 java 何时执行垃圾收集。

什么时候 java 执行垃圾收集?

1.当对象不再可达时:

BeginnersBook obj = new BeginnersBook();  
obj = null;

这里引用obj指向类BeginnersBook的对象,但由于我们为它分配了一个空值,因此它不再指向该对象,这使得BeginnersBook对象无法访问,因此无法使用。这些对象可自动用于 Java 中的垃圾收集。

另一个例子是:

char[] sayhello = { 'h', 'e', 'l', 'l', 'o'};
String str = new String(sayhello);
str = null;

这里String类的引用str指向堆内存中的字符串"hello",但由于我们已将空值赋给str,因此堆内存中存在的对象"hello"不可用。

2.将一个引用复制到另一个引用时:

BeginnersBook obj1 = new BeginnersBook();
BeginnersBook obj2 = new BeginnersBook();
obj2 = obj1;

这里我们已经将引用obj1分配给obj2,这意味着obj2指向的(引用)实例(对象)不可访问并且可用于垃圾收集。

如何请求 JVM 进行垃圾回收

我们现在知道无法访问和不可用的对象可用于垃圾收集,但垃圾收集过程不会立即发生。这意味着一旦对象准备好进行垃圾回收,他们就必须等待 JVM 运行执行垃圾收集的内存清理程序。但是,您可以通过调用System.gc()方法请求 JVM 进行垃圾回收(请参阅下面的示例)。

Java 中的垃圾收集示例

在这个例子中,我们通过调用System.gc()来演示垃圾收集。在这段代码中,我们重载了一个finalize()方法。在 Java 垃圾收集过程销毁对象之前调用此方法。这就是您在输出中看到此方法已被调用两次的原因。

public class JavaExample{   
   public static void main(String args[]){  
        /* Here we are intentionally assigning a null 
         * value to a reference so that the object becomes
         * non reachable
         */
	JavaExample obj=new JavaExample();  
	obj=null;  

        /* Here we are intentionally assigning reference a 
         * to the another reference b to make the object referenced
         * by b unusable.
         */
	JavaExample a = new JavaExample();
	JavaExample b = new JavaExample();
	b = a;
	System.gc();  
   }  
   protected void finalize() throws Throwable
   {
        System.out.println("Garbage collection is performed by JVM");
   }
}

输出:

Garbage collection is performed by JVM
Garbage collection is performed by JVM

Java 中的final关键字 - final变量,方法和类

原文: https://beginnersbook.com/2014/07/final-keyword-java-final-variable-method-class/

在本教程中,我们将学习final关键字的用法。final关键字可以与变量,方法和类一起使用。我们将详细介绍以下主题。

1)final变量

2)final方法

3)final

1)final变量

final变量只不过是常数。初始化后,我们无法更改final变量的值。让我们看看下面的代码:

class Demo{  

   final int MAX_VALUE=99;
   void myMethod(){  
      MAX_VALUE=101;
   }  
   public static void main(String args[]){  
      Demo obj=new  Demo();  
      obj.myMethod();  
   }  
}

输出:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	The final field Demo.MAX_VALUE cannot be assigned

	at beginnersbook.com.Demo.myMethod(Details.java:6)
	at beginnersbook.com.Demo.main(Details.java:10)

我们在上面的程序中遇到了编译错误,因为我们试图更改final变量MAX_VALUE的值。

注意:在大写(CAPS)中使用常量名称被认为是一种好习惯。

空白的final变量

在声明时未初始化的final变量称为空白final变量。我们必须在类的构造函数中初始化空白的final变量,否则会抛出编译错误(错误:变量MAX_VALUE可能尚未初始化)。

这是在类中使用空白final变量的方式:

class Demo{  
   //Blank final variable
   final int MAX_VALUE;

   Demo(){
      //It must be initialized in constructor
      MAX_VALUE=100;
   }
   void myMethod(){  
      System.out.println(MAX_VALUE);
   }  
   public static void main(String args[]){  
      Demo obj=new  Demo();  
      obj.myMethod();  
   }  
}

输出:

100

什么是空白的final变量?

假设我们有一个Student类,其中有一个名为Roll No的字段。由于Roll No不应该在学生注册后更改,我们可以将其声明为类中的final变量但我们无法提前为所有学生初始化Roll No(否则所有学生都会有相同的Roll No)。在这种情况下,我们可以声明Roll No变量为空白的final,我们在对象创建期间初始化此值,如下所示:

class StudentData{  
   //Blank final variable
   final int ROLL_NO;

   StudentData(int rnum){
      //It must be initialized in constructor
      ROLL_NO=rnum;
   }
   void myMethod(){  
      System.out.println("Roll no is:"+ROLL_NO);
   }  
   public static void main(String args[]){  
      StudentData obj=new  StudentData(1234);  
      obj.myMethod();  
   }  
}

输出:

Roll no is:1234

更多关于 StackOverflowWiki 的空白final变量。

未初始化的静态final变量

声明期间未初始化的静态final变量只能在静态块中初始化。例:

class Example{  
   //static blank final variable  
   static final int ROLL_NO;
   static{ 
      ROLL_NO=1230;
   }  
   public static void main(String args[]){  
      System.out.println(Example.ROLL_NO);  
   }  
}

输出:

1230

2)final方法

final方法无法覆盖。这意味着即使子类可以调用父类的final方法而没有任何问题,但它不能覆盖它。

例:

class XYZ{  
   final void demo(){
      System.out.println("XYZ Class Method");
   }  
}  

class ABC extends XYZ{  
   void demo(){
      System.out.println("ABC Class Method");
   }  

   public static void main(String args[]){  
      ABC obj= new ABC();  
      obj.demo();  
   }  
}

上面的程序会抛出一个编译错误,但是我们可以在子类中使用父类final方法而不会出现任何问题。让我们来看看这段代码:这个程序运行正常,因为我们没有覆盖final方法。这表明final方法可以继承,但它们不符合覆盖的条件。

class XYZ{  
   final void demo(){
      System.out.println("XYZ Class Method");
   }  
}  

class ABC extends XYZ{  
   public static void main(String args[]){  
      ABC obj= new ABC();  
      obj.demo();  
   }  
}

输出:

XYZ Class Method

3)final

我们不能扩展final类。考虑以下示例:

final class XYZ{  
}  

class ABC extends XYZ{  
   void demo(){
      System.out.println("My Method");
   }  
   public static void main(String args[]){  
      ABC obj= new ABC(); 
      obj.demo();
   }  
}

输出:

The type ABC cannot subclass the final class XYZ

要记住的要点:

1)构造函数不能被声明为 final。

2)本地final变量必须在声明期间初始化。

3)接口中声明的所有变量默认为 final。

4)我们无法改变final变量的值。

5)final方法不能被覆盖。

6)final类不被继承。

7)如果方法参数被声明为final,则不能更改这些参数的值。

8)以所有大写字母命名final变量是一个好习惯。

9)finalfinallyfinalize确定了三个不同的术语。finally用于异常处理,finalize是在垃圾收集期间由 JVM 调用的方法。

Java 异常处理教程

Java 虚拟机(JVM),JDK 差异,JRE 和 JVM - 核心 Java

原文: https://beginnersbook.com/2013/05/jvm/

Java 是一种高级编程语言。用高级语言编写的程序不能直接在任何机器上运行。首先,需要将其翻译成特定的机器语言。 javac编译器做了这件事,它需要 java 程序(包含源代码的.java文件)并将其转换为机器代码(称为字节代码或.class文件)。

Java 虚拟机(JVM)是​​驻留在真实机器(您的计算机)中的虚拟机,而 JVM 的机器语言是字节代码。这使编译器更容易,因为它必须为 JVM 生成字节代码,而不是为每种类型的机器生成不同的机器代码。 JVM 执行编译器生成的字节代码并生成输出。 JVM 是独立于 Java 平台的

所以,现在我们知道 JVM 的主要功能是执行编译器生成的字节代码。 每个操作系统都有不同的 JVM,但是在执行字节代码后它们产生的输出在所有操作系统中都是相同的。 这意味着在 Windows 上生成的字节代码可以在 Mac OS 上运行,反之亦然。这就是我们将 java 称为平台无关语言的原因。同样的事情可以在下图中看到:

JVM

总结一切: Java 虚拟机(JVM)是​​在实际机器(您的计算机)上运行并执行 Java 字节代码的虚拟机。 JVM 不了解 Java 源代码,这就是我们需要使用javac编译器来编译.java文件以获取包含 JVM 理解的字节代码的.class文件的原因。 JVM 使 java 可移植(一次编写,随处运行)。每个操作系统都有不同的 JVM,但是在执行字节代码后它们产生的输出在所有操作系统中都是相同的。

JVM 架构

jvm architecture

让我们看看 JVM 是如何工作的

类加载器:类加载器读取.class文件并将字节代码保存在方法区域中

方法区:JVM 中只有一个方法区域在所有类之间共享。这保存了每个.class文件的类级别信息。

:堆是分配对象的 JVM 内存的一部分。 JVM 为每个.class文件创建一个Class对象。

Stack :栈也是 JVM 内存的一部分,但与堆不同,它用于存储临时变量。

PC 寄存器:用于跟踪已执行的指令以及将要执行的指令。由于指令由线程执行,因此每个线程都有一个单独的 PC 寄存器。

本地方法堆栈:本机方法可以访问虚拟机的运行时数据区域。

本地方法接口:它使 java 代码能够被本机应用调用或调用。本机应用是特定于系统的硬件和操作系统的程序。

垃圾收集:类代码由 java 代码显式创建,使用后会被垃圾收集自动销毁以进行内存管理。

JVM 与 JRE 和 JDK 对比

JRE:JRE 是 java 虚拟机运行的环境。 JRE 包含 Java 虚拟机(JVM),类库和其他文件,不包括编译器和调试器等开发工具。
这意味着您可以在 JRE 中运行代码,但无法在 JRE 中开发和编译代码。

JVM:如上所述,JVM 使用 JRE 提供的类,库和文件来运行程序。

JRE

JDK:JDK 是 JRE 的超集,它包含 JRE 与开发工具(如编译器,调试器等)的所有内容。

JDK

java 中的异常处理

原文: https://beginnersbook.com/2013/04/java-exception-handling/

异常处理是 java 编程最重要的特性之一,它允许我们处理异常引起的运行时错误。在本指南中,我们将学习什么是异常,它的类型,异常类以及如何使用示例处理 java 中的异常。

什么是异常?

异常是一个不需要的事件,它会中断程序的正常流程。当发生异常时,程序执行将终止。在这种情况下,我们会收到系统生成的错误消息。关于异常的好处是它们可以用 Java 来处理。通过处理异常,我们可以向用户提供有关问题的有意义的消息,而不是系统生成的消息,这可能是用户无法理解的。

为什么会发生异常?

可能有几个原因导致程序抛出异常。例如:在程序中打开一个不存在的文件,网络连接问题,用户提供的错误输入数据等。

异常处理

如果发生异常(程序员尚未处理),则程序执行将终止,并向用户显示系统生成的错误消息。例如,查看下面的系统生成异常:
系统生成的异常在下面给出

 Exception in thread "main" java.lang.ArithmeticException: / by zero at ExceptionDemo.main(ExceptionDemo.java:5)
 ExceptionDemo : The class name
 main : The method name
 ExceptionDemo.java : The filename
 java:5 : Line number

此消息不是用户友好的,因此用户将无法理解出错的地方。为了让他们用简单的语言知道原因,我们处理异常。我们处理这些条件,然后向用户输出用户友好的警告消息,这使得他们可以纠正错误,因为大多数时候由于用户提供的错误数据而发生异常。

异常处理的优点

异常处理可确保在发生异常时程序流不会中断。例如,如果一个程序有一堆语句,并且在执行某些语句后中途发生异常,则异常后的语句将不会执行,程序将突然终止。
通过处理,我们确保所有语句都执行,程序流程不会中断。

错误和异常之间的区别

错误表示出现了严重问题,应用应该崩溃而不是尝试处理错误。

异常是代码中发生的事件。程序员可以处理这些情况并采取必要的纠正措施。几个例子:
NullPointerException - 当你尝试使用指向null的引用时。
ArithmeticException - 当用户提供错误数据时,例如,当您尝试将数字除以零时,会发生此异常,因为将数字除以零是未定义的。
ArrayIndexOutOfBoundsException - 当您尝试从其边界外访问数组的元素时,例如,数组大小为 5(这意味着它有五个元素),并且您尝试访问第 10 个元素。

Exception classes hierarchy

异常的类型

Java 中有两种类型的异常:

1)受检的异常

2)非受检的异常

我已在单独的教程中详细介绍了这一点: Java 中的受检和非受检异常。

受检异常

除运行时异常之外的所有异常都称为受检异常,因为编译器在编译期间检查它们以查看程序员是否已处理它们。如果在程序中未处理/声明这些异常,则会出现编译错误。例如,SQLExceptionIOExceptionClassNotFoundException等。

非受检的异常情况

运行时异常也称为非受检的异常。这些异常不会在编译时检查,因此编译器不会检查程序员是否已经处理过它们,但程序员有责任处理这些异常并提供安全退出。例如,ArithmeticExceptionNullPointerExceptionArrayIndexOutOfBoundsException等。

编译器永远不会强制您捕获此类异常或强制您使用throws关键字在方法中声明它。

下一个教程将介绍哪些主题

  1. Java 中的try-catch
  2. 嵌套的Try Catch
  3. 受检和非受检的异常
  4. Java 中的finally
  5. try-catch-finally
  6. finally块和return语句
  7. 在 Java 中抛出异常
  8. throw关键字的示例
  9. throw关键字的例子
  10. throws
  11. throw vs throws
  12. 异常处理示例

Java 中的try-catch - 异常处理

原文: https://beginnersbook.com/2013/04/try-catch-in-java/

上一篇教程中,我们讨论了什么是异常处理以及我们为什么这样做。在本教程中,我们将看到用于异常处理的try-catch块。

try

try块包含可以发生异常的一组语句。try块后面总是跟一个catch块,它处理相关try块中发生的异常。try块必须后跟catch块或finally块或两者。

try块的语法

try{
   //statements that may cause an exception
}

在编写程序时,如果您认为程序中的某些语句可以抛出异常,请将它们包含在try块中并处理该异常

catch

catch块是处理异常的地方,此块必须遵循try块。单个try块可以有几个与之关联的catch块。您可以在不同的catch块中捕获不同的异常。当try块中发生异常时,将执行处理该特定异常的相应catch块。例如,如果在try块中发生算术异常,则执行catch块中用于算术异常的语句。

java 中try-catch的语法

try
{
     //statements that may cause an exception
}
catch (exception(type) e(object))‏
{
     //error handling code
}

示例:try-catch

如果try块中发生异常,则执行控制将传递给相应的catch块。单个try块可以有多个与之关联的catch块,您应该放置catch块,使得通用异常处理程序catch块位于最后(参见下面的示例)。

通用异常处理程序可以处理所有异常但是你应该放在最后,如果你把它放在所有catch块之前,那么它将显示通用消息。您始终希望为每种类型的异常提供有意义的消息,而不是通用消息。

class Example1 {
   public static void main(String args[]) {
      int num1, num2;
      try {
         /* We suspect that this block of statement can throw 
          * exception so we handled it by placing these statements
          * inside try and handled the exception in catch block
          */
         num1 = 0;
         num2 = 62 / num1;
         System.out.println(num2);
         System.out.println("Hey I'm at the end of try block");
      }
      catch (ArithmeticException e) { 
         /* This block will only execute if any Arithmetic exception 
          * occurs in try block
          */
         System.out.println("You should not divide a number by zero");
      }
      catch (Exception e) {
         /* This is a generic Exception handler which means it can handle
          * all the exceptions. This will execute if the exception is not
          * handled by previous catch blocks.
          */
         System.out.println("Exception occurred");
      }
      System.out.println("I'm out of try-catch block in Java.");
   }
}

输出:

You should not divide a number by zero
I'm out of try-catch block in Java.

Java 中的多个catch

我们在上面看到的示例是有多个catch块,让我们在示例的帮助下看到关于多个catch块的一些规则。要详细阅读,请参阅在 java 中捕获多个异常

  1. 如上所述,单个try块可以包含任意数量的catch块。
  2. 通用catch块可以处理所有异常。无论是ArrayIndexOutOfBoundsException还是ArithmeticExceptionNullPointerException或任何其他类型的异常,它都会处理所有这些异常。要查看NullPointerExceptionArrayIndexOutOfBoundsException的示例,请参阅以下文章:异常处理示例程序
catch(Exception e){
  //This catch block catches all the exceptions
}

如果你想知道为什么我们需要其他捕获处理程序,当我们有一个可以处理所有的通用。这是因为在通用异常处理程序中,您可以显示消息,但您不确定它可能触发的异常类型,因此它将为所有异常显示相同的消息,并且用户可能无法理解发生了哪个异常。这就是你应该放置的原因是在所有特定异常catch块的末尾

  1. 如果try块中没有异常,则完全忽略catch块。
  2. 对应的catch块执行特定类型的异常:
    catch(ArithmeticException e)是一个可以解决ArithmeticExceptioncatch
    catch(NullPointerException e)是一个可以处理NullPointerExceptioncatch
  3. 你也可以抛出异常,这是一个高级主题,我在单独的教程中介绍了它:用户定义异常throw关键字throw vs throws

多个catch块的示例

class Example2{
   public static void main(String args[]){
     try{
         int a[]=new int[7];
         a[4]=30/0;
         System.out.println("First print statement in try block");
     }
     catch(ArithmeticException e){
        System.out.println("Warning: ArithmeticException");
     }
     catch(ArrayIndexOutOfBoundsException e){
        System.out.println("Warning: ArrayIndexOutOfBoundsException");
     }
     catch(Exception e){
        System.out.println("Warning: Some Other exception");
     }
   System.out.println("Out of try-catch block...");
  }
}

输出:

Warning: ArithmeticException
Out of try-catch block...

在上面的示例中,有多个catch块,当try块中发生异常时,这些catch块按顺序执行。这意味着如果你把最后一个catch块(catch(Exception e))放在第一个地方,就在try块之后,那么在任何异常的情况下,这个块将执行,因为它可以处理所有异常。该挡块应放在最后,以避免这种情况。

finally

我在这里单独介绍了这个:java finally。现在你只需知道这个块执行是否发生异常。您应该将这些语句放在finally块中,必须执行是否发生异常。

Java finally块 - 异常处理

原文: https://beginnersbook.com/2013/04/java-finally-block/

在之前的教程中,我介绍了try-catch嵌套的try。在本指南中,我们将看到finally trytry-catch一起使用。

finally包含所有必须执行的关键语句,无论是否发生异常。无论 try 块是否发生异常,例如关闭连接,流等,此块中的语句将始终执行。

finally块的语法

try {
    //Statements that may cause an exception
}
catch {
   //Handling exception
}
finally {
   //Statements to be executed
}

finally块的简单示例

在这里你可以看到异常发生在try块中,它已经在catch块中被处理,在finally块被执行之后。

class Example
{
   public static void main(String args[]) {
      try{  
	 int num=121/0;  
	 System.out.println(num);  
      }  
      catch(ArithmeticException e){
         System.out.println("Number should not be divided by zero");
      }  
      /* Finally block will always execute
       * even if there is no exception in try block
       */
      finally{
	 System.out.println("This is finally block");
      }  
      System.out.println("Out of try-catch-finally");  
   }   
}

输出:

Number should not be divided by zero
This is finally block
Out of try-catch-finally

finally块的几个重点

  1. finally块必须与try块相关联,如果没有try块,则不能使用finally块。您应该将这些语句放在必须始终执行的块中。

  2. finally块是可选的,正如我们在前面的教程中看到的那样,try-catch块足以用于异常处理,但是如果你放置一个finally块,那么它总是在执行try块后运行。

  3. 在正常情况下,当try块中没有异常时,则在try块之后执行finally块。但是,如果发生异常,则在finally块之前执行catch块。

  4. finally块中的异常行为与任何其他异常完全相同。

  5. 即使try块包含诸如returnbreakcontinue之类的控制转移语句,finally块中的语句最终也会执行。
    让我们看一个例子,看看当try块中存在return语句时最终是如何工作的:

finally块和return语句的另一个例子

你可以看到,即使我们在方法中有return语句,finally块仍然会运行。

class JavaFinally
{
   public static void main(String args[])
   {
      System.out.println(JavaFinally.myMethod());  
   }
   public static int myMethod()
   {
      try {
        return 112;
      }
      finally {
        System.out.println("This is Finally block");
        System.out.println("Finally block ran even after return statement");
      }
   }
}

以上程序的输出:

This is Finally block
Finally block ran even after return statement
112

要查看finallyreturn的更多示例,请参阅: Java finally块和返回语句

finally块未执行时的情况

阻止在finally块中执行代码的情况是:

  • 线程的死亡
  • 使用System.exit()方法。
  • 由于finally块中出现异常。

finallyclose()

close()语句用于关闭程序中的所有打开流。在finally块中使用close()是一个很好的做法。由于即使发生异常,最终块也会执行,因此无论是否发生异常,您都可以确保所有输入和输出流都已正确关闭。

例如:

....
try{ 
    OutputStream osf = new FileOutputStream( "filename" );
    OutputStream osb = new BufferedOutputStream(opf);
    ObjectOutput op = new ObjectOutputStream(osb);
    try{
       output.writeObject(writableObject);
    }
    finally{
       op.close();
    }
}
catch(IOException e1){
     System.out.println(e1);
}
...

没有catchfinally

可以在没有catch块的情况下使用try-finally块。这意味着try块可以在没有catch块的情况下使用。

...
InputStream input = null;
try {
    input = new FileInputStream("inputfile.txt");
} 
finally {
    if (input != null) {
       try {
         in.close();
       }catch (IOException exp) {
           System.out.println(exp);
        }
    }
}
...

finally块和System.exit()

System.exit()语句的行为与return语句不同。与return语句不同,每当在try块中调用System.exit()时,finally块不会执行。这是一个代码片段,演示了相同的代码:

....
try {
   //try block
   System.out.println("Inside try block");
   System.exit(0)
}
catch (Exception exp) {
   System.out.println(exp);
}
finally {
   System.out.println("Java finally block");
}
....

在上面的例子中,如果System.exit(0)被调用而没有任何异常,那么最终将不会执行。但是,如果在调用System.exit(0)时发生任何异常,则将执行finally块。

try-catch-finally

  • try语句应该与catch块或finally相关联。
  • 由于catch执行异常处理并最终执行清理,因此最好的方法是同时使用它们。

语法:

try {
     //statements that may cause an exception
}
catch (…)‏ {
     //error handling code
}
finally {
    //statements to be executed
}

try-catch-finally块的例子

示例 1:以下示例演示了try块中没有异常时finally块的工作情况

class Example1{
  public static void main(String args[]){
    try{
       System.out.println("First statement of try block");
       int num=45/3;
       System.out.println(num);
    }
    catch(ArrayIndexOutOfBoundsException e){
       System.out.println("ArrayIndexOutOfBoundsException");
    }
    finally{
       System.out.println("finally block");
    }
    System.out.println("Out of try-catch-finally block");
  }
}

输出:

First statement of try block
15
finally block
Out of try-catch-finally block

示例 2:此示例显示了在try块中发生异常但在catch块中未处理时finally块的工作:

class Example2{
   public static void main(String args[]){
     try{
        System.out.println("First statement of try block");
        int num=45/0;
        System.out.println(num);
     }
     catch(ArrayIndexOutOfBoundsException e){
        System.out.println("ArrayIndexOutOfBoundsException");
     }
     finally{
        System.out.println("finally block");
     }
     System.out.println("Out of try-catch-finally block");
   }
}

输出:

First statement of try block
finally block
Exception in thread "main" java.lang.ArithmeticException: / by zero
at beginnersbook.com.Example2.main(Details.java:6)

正如您所看到的那样,系统生成了异常消息,但在此之前,finally块已成功执行。

例 3 :当try块发生异常并在catch块中正确处理时

class Example3{
   public static void main(String args[]){
      try{
         System.out.println("First statement of try block");
         int num=45/0;
         System.out.println(num);
      }
      catch(ArithmeticException e){
         System.out.println("ArithmeticException");
      }
      finally{
         System.out.println("finally block");
      }
      System.out.println("Out of try-catch-finally block");
   }
}

输出:

First statement of try block
ArithmeticException
finally block
Out of try-catch-finally block

如何在 java 中抛出异常

原文: https://beginnersbook.com/2013/04/throw-in-java/

在 Java 中,我们已经定义了异常类,例如ArithmeticExceptionNullPointerExceptionArrayIndexOutOfBounds异常等。这些异常被设置为在不同的条件下触发。例如,当我们将一个数除以零时,这会触发ArithmeticException,当我们尝试从其边界中访问数组元素时,我们得到ArrayIndexOutOfBoundsException

我们可以定义自己的条件或规则集,并使用throw关键字显式抛出异常。例如,当我们将数字除以 5 或任何其他数字时,我们可以抛出ArithmeticException,我们需要做的只是设置条件并使用throw关键字抛出任何异常。Throw关键字也可用于抛出自定义异常,我已在单独的教程中介绍过,请参阅 Java 中的自定义异常

throw关键字语法:

throw new exception_class("error message");

例如:

throw new ArithmeticException("dividing a number by 5 is not allowed in this program");

throw关键字的示例

假设我们有一个要求,我们只需要在年龄小于 12 且体重小于 40 的情况下注册学生,如果不满足任何条件,那么用户应该获得带有警告消息“学生没有资格注册”的ArithmeticException。我们已经通过将代码放在检查学生资格的方法中来实现逻辑,如果输入的学生年龄和体重不符合标准,那么我们使用throw关键字抛出异常。

/* In this program we are checking the Student age
 * if the student age<12 and weight <40 then our program 
 * should return that the student is not eligible for registration.
 */
public class ThrowExample {
   static void checkEligibilty(int stuage, int stuweight){ 
      if(stuage<12 && stuweight<40) {
         throw new ArithmeticException("Student is not eligible for registration"); 
      }
      else {
         System.out.println("Student Entry is Valid!!"); 
      }
   } 

   public static void main(String args[]){ 
     System.out.println("Welcome to the Registration process!!");
     checkEligibilty(10, 39); 
     System.out.println("Have a nice day.."); 
 } 
}

输出:

Welcome to the Registration process!!Exception in thread "main" 
java.lang.ArithmeticException: Student is not eligible for registration
at beginnersbook.com.ThrowExample.checkEligibilty(ThrowExample.java:9)
at beginnersbook.com.ThrowExample.main(ThrowExample.java:18)

在上面的例子中,我们抛出了一个非受检的异常,同样地,我们可以抛出非受检用户定义的异常

有关throw关键字的更多示例,请参阅:throw关键字示例

java 中的用户定义的异常

原文: https://beginnersbook.com/2013/04/user-defined-exception-in-java/

在 java 中我们已经定义了异常类,例如ArithmeticExceptionNullPointerException等。这些异常已经设置为在预定义条件下触发,例如当你将数字除以零时它会触发ArithmeticException,在上一个教程中我们学习了如何抛出这些异常根据您使用throw关键字的条件明确显示。

在 java 中,我们可以创建自己的异常类,并使用throw关键字抛出该异常。这些异常称为用户定义的自定义异常。在本教程中,我们将了解如何创建自己的自定义异常并将其抛出到特定条件。

要理解本教程,您应该具备try-catch java 中的throw 的基本知识。

Java 中用户定义的异常示例

/* This is my Exception class, I have named it MyException
 * you can give any name, just remember that it should
 * extend Exception class
 */
class MyException extends Exception{
   String str1;
   /* Constructor of custom exception class
    * here I am copying the message that we are passing while
    * throwing the exception to a string and then displaying 
    * that string along with the message.
    */
   MyException(String str2) {
	str1=str2;
   }
   public String toString(){ 
	return ("MyException Occurred: "+str1) ;
   }
}

class Example1{
   public static void main(String args[]){
	try{
		System.out.println("Starting of try block");
		// I'm throwing the custom exception using throw
		throw new MyException("This is My error Message");
	}
	catch(MyException exp){
		System.out.println("Catch Block") ;
		System.out.println(exp) ;
	}
   }
}

输出:

Starting of try block
Catch Block
MyException Occurred: This is My error Message

说明:

你可以看到,在抛出自定义异常时,我在括号中给出了一个字符串(throw new MyException("This is My error Message");)。这就是为什么我的自定义异常类中有参数化构造函数(带有String参数)的原因。

注意:

  1. 用户定义的异常必须扩展Exception类。
  2. 使用throw关键字抛出异常。

自定义异常的另一个例子

在这个例子中,我们从方法中抛出一个异常。在这种情况下,我们应该在方法签名中使用throws子句,否则你将得到编译错误,说“方法中未处理的异常”。要了解throws子句的工作原理,请参考本指南:java 中的throw关键字

class InvalidProductException extends Exception
{
    public InvalidProductException(String s)
    {
        // Call constructor of parent Exception
        super(s);
    }
}

public class Example1
{
   void productCheck(int weight) throws InvalidProductException{
	if(weight<100){
		throw new InvalidProductException("Product Invalid");
	}
   }

    public static void main(String args[])
    {
    	Example1 obj = new Example1();
        try
        {
            obj.productCheck(60);
        }
        catch (InvalidProductException ex)
        {
            System.out.println("Caught the exception");
            System.out.println(ex.getMessage());
        }
    }
}

输出:

Caught the exception
Product Invalid

Java 异常处理

原文: https://beginnersbook.com/2013/04/exception-handling-examples/

在本教程中,我们将看到几个常用异常的示例。如果您正在寻找异常处理教程,请参阅此完整指南:Java 中的异常处理

示例 1:算术异常

类:Java.lang.ArithmeticException
这是java.lang包中的内置类。当整数除以零时会发生此异常。

class Example1
{
   public static void main(String args[])
   {
      try{
         int num1=30, num2=0;
         int output=num1/num2;
         System.out.println ("Result: "+output);
      }
      catch(ArithmeticException e){
         System.out.println ("You Shouldn't divide a number by zero");
      }
   }
}

以上程序的输出:

You Shouldn't divide a number by zero

说明:在上面的例子中,我将整数除以零,因此抛出ArithmeticException

示例 2:ArrayIndexOutOfBounds异常

类:Java.lang.ArrayIndexOutOfBoundsException
当您尝试访问不存在的数组索引时,会发生此异常。例如,如果数组只有 5 个元素,并且我们试图显示第 7 个元素,那么它将抛出此异常。

class ExceptionDemo2
{
   public static void main(String args[])
   {
      try{
        int a[]=new int[10];
        //Array has only 10 elements
        a[11] = 9;
      }
      catch(ArrayIndexOutOfBoundsException e){
         System.out.println ("ArrayIndexOutOfBounds");
      }
   }
}

输出:

ArrayIndexOutOfBounds

在上面的示例中,数组被初始化为仅存储 10 个元素索引 0 到 9.因为我们尝试访问索引 11 的元素,所以程序抛出此异常。

示例 3:NumberFormatException

分类:Java.lang.NumberFormatException

将字符串解析为任何数字变量时会发生此异常。

例如,语句int num=Integer.parseInt ("XYZ");将抛出NumberFormatException,因为String "XYZ"无法解析为int

class ExceptionDemo3
{
   public static void main(String args[])
   {
      try{
	 int num=Integer.parseInt ("XYZ") ;
	 System.out.println(num);
      }catch(NumberFormatException e){
	  System.out.println("Number format exception occurred");
       }
   }
}

输出:

Number format exception occurred

示例 4:StringIndexOutOfBound异常

分类:Java.lang.StringIndexOutOfBoundsException

  • 每当调用一个不在范围内的字符串的索引时,就会创建此类的对象。
  • 字符串对象的每个字符都存储在从 0 开始的特定索引中。
  • 要获得字符串的特定索引中存在的字符,我们可以使用java.lang.StringcharAt(int)方法,其中int参数是索引。

例如。

class ExceptionDemo4
{
   public static void main(String args[])
   {
      try{
	 String str="beginnersbook";
	 System.out.println(str.length());;
	 char c = str.charAt(0);
	 c = str.charAt(40);
	 System.out.println(c);
      }catch(StringIndexOutOfBoundsException e){
	  System.out.println("StringIndexOutOfBoundsException!!");
       }
   }
}

输出:

13
StringIndexOutOfBoundsException!!

发生异常是因为String中没有引用的索引。

示例 5:NullPointer异常

类:Java.lang.NullPointer Exception
只要使用null对象调用成员,就会创建此类的对象。

class Exception2 
{
   public static void main(String args[])
   {
	try{
		String str=null;
		System.out.println (str.length());
	}
        catch(NullPointerException e){
		System.out.println("NullPointerException..");
	}
   }
}

输出:

NullPointerException..

这里,length()是函数,应该在对象上使用。但是在上面的示例中String对象str为空,因此它不是由于NullPointerException发生的对象。

Java 注解,枚举和正则表达式教程

Java 枚举教程

原文: https://beginnersbook.com/2014/09/java-enum-examples/

枚举是一种特殊类型的数据类型,它基本上是常量的集合(集合)。在本教程中,我们将学习如何在 Java 中使用枚举以及我们可以使用它们的可能场景。

这就是我们定义Enum的方式

public enum Directions{
  EAST, 
  WEST, 
  NORTH, 
  SOUTH
}

这里我们有枚举类型的变量方向,它是四个常数EASTWESTNORTHSOUTH的集合。

如何为枚举类型赋值?

Directions dir = Directions.NORTH;

变量dir的类型为Directions(即枚举类型)。此变量可以取可能的四个值(EASTWESTNORTHSOUTH)中的任何值。在这种情况下,它设置为NORTH

if-else语句中使用Enum类型

这就是我们如何在if-else逻辑中使用枚举变量。

/* You can assign any value here out of
 * EAST, WEST, NORTH, SOUTH. Just for the
 * sake of example, I'm assigning to NORTH
 */
Directions dir = Directions.NORTH;  

if(dir == Directions.EAST) {
  // Do something. Write your logic
} else if(dir == Directions.WEST) {
     // Do something else
  } else if(dir == Directions.NORTH) {
     // Do something 
    } else {
        /* Do Something. Write logic for 
         * the remaining constant SOUTH
         */ 
      }

枚举示例

这只是演示使用枚举的一个示例。如果您了解核心部分和基础知识,您就可以根据需求编写自己的逻辑。

public enum Directions{
	  EAST, 
	  WEST, 
	  NORTH, 
	  SOUTH
}
public class EnumDemo
{
   public static void main(String args[]){
	Directions dir = Directions.NORTH;  
	if(dir == Directions.EAST) {
	    System.out.println("Direction: East");
	} else if(dir == Directions.WEST) {
	    System.out.println("Direction: West");
	  } else if(dir == Directions.NORTH) {
	      System.out.println("Direction: North");
  	    } else {
		System.out.println("Direction: South");
	      }
   }
}

输出:

Direction: North

Switch-Case语句中使用Enum

下面是演示在switch-case语句中使用枚举的示例。

public enum Directions{
	  EAST, 
	  WEST, 
	  NORTH, 
	  SOUTH
}
public class EnumDemo
{
   Directions dir;
   public EnumDemo(Directions dir) {
      this.dir = dir;
   }
   public void getMyDirection() {
     switch (dir) {
       case EAST:
          System.out.println("In East Direction");
          break;

       case WEST:
          System.out.println("In West Direction");
          break;

       case NORTH: 
          System.out.println("In North Direction");
          break;

       default:
          System.out.println("In South Direction");
          break;
     }
   }

    public static void main(String[] args) {
        EnumDemo obj1 = new EnumDemo(Directions.EAST);
        obj1.getMyDirection();
        EnumDemo obj2 = new EnumDemo(Directions.SOUTH);
        obj2.getMyDirection();
    }
}

输出:

In East Direction
In South Direction

如何遍历Enum变量

class EnumDemo
{
    public static void main(String[] args) {
    	for (Directions dir : Directions.values()) {
    	    System.out.println(dir);
    	}
    }
}

此代码将显示所有四个常量。

枚举字段和方法

让我们先举一个例子然后我们将详细讨论它:

public enum Directions{
  EAST ("E"), 
  WEST ("W"), 
  NORTH ("N"), 
  SOUTH ("S")
  ; 
  /* Important Note: Must have semicolon at
   * the end when there is a enum field or method
   */
  private final String shortCode;

  Directions(String code) {
      this.shortCode = code;
  }

  public String getDirectionCode() {
      return this.shortCode;
  }
}
public class EnumDemo
{
    public static void main(String[] args) {
    	Directions dir = Directions.SOUTH;
    	System.out.println(dir.getDirectionCode());
    	Directions dir2 = Directions.EAST;
    	System.out.println(dir2.getDirectionCode());
    }
}

输出:

S
E

正如您在本示例中所看到的,我们为每个常量都有一个字段shortCode,以及一个方法getDirectionCode(),它基本上是该字段的获取器方法。当我们定义一个像EAST ("E")这样的常量时,它会使用传递的参数调用枚举构造函数(参见上例中的构造函数Directions)。这样,传递的值被设置为相应枚举常数EAST("E")的字段的值,它会调用构造函数Directions("E")this.shortCode = code,即this.shortCode = "E",常数EASTshortCode字段设置为"E"

注意事项:

1)在定义枚举时,应在任何字段或方法之前首先声明常量。

2)当在Enum中声明了字段和方法时,枚举常量列表必须以分号(;)结尾。

Java 注解教程

原文: https://beginnersbook.com/2014/09/java-annotations/

Java 注解允许我们将元数据信息添加到我们的源代码中,尽管它们不是程序本身的一部分。注解从 JDK 5 添加到 java 中。注解对它们注解的代码的操作没有直接影响(即它不影响程序的执行)。

在本教程中,我们将介绍以下主题:注解的使用,如何应用注解,Java 中可用的预定义注解类型以及如何创建自定义注解。

注解有什么用?

1)编译器指令:Java 中有三种内置注解(@Deprecated@Override@SuppressWarnings),可用于向编译器提供某些指令。例如,@override注解用于指示编译器注解方法是否覆盖该方法。有关这些内置注解的更多信息,请参阅本文的下一部分。

2)编译时教程:注解可以为编译器提供编译时指令,软件构建工具可以进一步使用它来生成代码,XML 文件等。

3)运行时指令:我们可以定义在运行时可用的注解,我们可以使用 java 反射访问它们,并可用于在运行时向程序发出指令。我们将在稍后的同一篇文章的帮助下讨论这个问题。

注解基础知识

注解始终以符号@开头,后跟注解名称。符号@向编译器指示这是一个注解。

对于例如@Override
这里@符号表示这是一个注解,而Override是这个注解的名称。

我们可以在哪里使用注解?

注解可以应用于类,接口,方法和字段。例如,以下注解正在应用于该方法。

@Override
void myMethod() { 
    //Do something 
}

这个注解在这里做的正是在下一节中解释的,但简单来说它指示编译器myMethod()是一个覆盖超类方法(myMethod())的重写方法。

Java 中的内置注解

Java 有三个内置注解:

  • @Override
  • @Deprecated
  • @SuppressWarnings

1)@Override

在覆盖子类中的方法时,我们应该使用此批注来标记该方法。这使得代码可读并避免维护问题,例如:在更改父类的方法签名时,必须更改子类中的签名(使用此批注的位置),否则编译器将抛出编译错误。如果您没有使用此注解,则很难跟踪。

例:

public class MyParentClass {

    public void justaMethod() {
        System.out.println("Parent class method");
    }
}

public class MyChildClass extends MyParentClass {

    @Override
    public void justaMethod() {
        System.out.println("Child class method");
    }
}

我相信这个例子是自我解释的。要阅读有关此注解的更多信息,请参阅以下文章: `@Override内置注解

2)@Deprecated

@Deprecated注解表示已弃用标记的元素(类,方法或字段),不应再使用。只要程序使用已经使用@Deprecated注解标记的方法,类或字段,编译器就会生成警告。不推荐使用元素时,也应使用 Javadoc @deprecated标记对其进行记录,如以下示例所示。记下@Deprecated@deprecated的大小写差异。@deprecated用于文档目的。

示例:

/**
 * @deprecated
 * reason for why it was deprecated
 */
@Deprecated
public void anyMethodHere(){
    // Do something
}

现在,只要任何程序使用此方法,编译器就会生成警告。要阅读有关此注解的更多信息,请参阅以下文章: Java - @Deprecated注解

3)@SuppressWarnings

此批注指示编译器忽略特定警告。例如,在下面的代码中,我调用了一个不推荐使用的方法(假设方法deprecatedMethod()标有@Deprecated注解),因此编译器应该生成警告,但是我使用@SuppressWarnings注解来抑制弃用警告。

@SuppressWarnings("deprecation")
    void myMethod() {
        myObject.deprecatedMethod();
}

创建自定义注解

  • 注解是使用@interface创建的,后跟注解名称,如下例所示。
  • 注解也可以包含元素。他们看起来像方法。例如,在下面的代码中,我们有四个元素。我们不应该为这些元素提供实现。
  • 所有注解都扩展了java.lang.annotation.Annotation接口。注解不能包含任何extends子句。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation{
    int studentAge() default 18;
    String studentName();
    String stuAddress();
    String stuStream() default "CSE";
}

注意:使用注解时,可以跳过在创建注解时设置了默认值的所有元素。例如,如果我将上述注解应用于类,那么我会这样做:

@MyCustomAnnotation(
    studentName="Chaitanya",
    stuAddress="Agra, India"
)
public class MyClass {
...
}

如您所见,我们没有给studentAgestuStream元素赋予任何值,因为设置这些元素的值是可选的(默认值已经在注解定义中设置,但是如果您希望可以分配新的使用注解时的值与我们对其他元素的处理方式相同)。但是,我们必须在使用注解时提供其他元素的值(没有设置默认值的元素)。

注意:我们也可以在注解中包含数组元素。这就是我们如何使用它们:
注解定义:

@interface MyCustomAnnotation {
    int      count();
    String[] books();
}

用法:

@MyCustomAnnotation(
    count=3,
    books={"C++", "Java"}
)
public class MyClass {

}

再次回到主题:在自定义注解示例中,我们使用了这四个注解:@Documented@Target@Inherited@Retention。让我们详细讨论它们。

@Documented

@Documented注解表明使用此注解的元素应由 JavaDoc 记录。例如:

java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation {
  //Annotation body
}
@MyCustomAnnotation
public class MyClass { 
     //Class body
}

在为类MyClass生成 javadoc 时,注解@MyCustomAnnotation将包含在其中。

@Target

它指定了我们可以使用注解的位置。例如:在下面的代码中,我们将目标类型定义为METHOD,这意味着下面的注解只能用于方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
public @interface MyCustomAnnotation {

}
public class MyClass {
   @MyCustomAnnotation
   public void myMethod()
   {
       //Doing something
   }
}

注意:1)如果您没有定义任何目标类型,则意味着可以将注解应用于任何元素。

2)除了ElementType.METHOD之外,注解可以具有以下可能的Target值。

ElementType.METHOD
ElementType.PACKAGE
ElementType.PARAMETER
ElementType.TYPE
ElementType.ANNOTATION_TYPE
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.FIELD

@Inherited

@Inherited注解表示类中使用的自定义注解应该由其所有子类继承。例如:

java.lang.annotation.Inherited

@Inherited
public @interface MyCustomAnnotation {

}
@MyCustomAnnotation
public class MyParentClass { 
  ... 
}
public class MyChildClass extends MyParentClass { 
   ... 
}

这里的类MyParentClass正在使用注解@MyCustomAnnotation,该注解用@inherited注解标记。这意味着子类MyChildClass继承了@MyCustomAnnotation

@Retention

它指示要保留带注解类型的注解的时间长度。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface MyCustomAnnotation {

}

这里我们使用了RetentionPolicy.RUNTIME。还有另外两种选择。让我们看看他们的意思:
RetentionPolicy.RUNTIME:注解应该在运行时可用,以便通过 java 反射进行检查。
RetentionPolicy.CLASS:注解将在.class文件中,但在运行时不可用。
RetentionPolicy.SOURCE:注解将在程序的源代码中提供,它既不在.class文件中,也不在运行时可用。

这就是“Java 注解”这个主题的全部内容。如果您有任何疑问,请随时在下面留言。

Java 正则表达式教程

原文: https://beginnersbook.com/2014/08/java-regex-tutorial/

正则表达式用于定义可用于搜索,操作和编辑文本的字符串模式。这些表达式也称为 Regex (正则表达式的简写形式)。

让我们举个例子来更好地理解它:

在下面的示例中,正则表达式.*book.*用于搜索文本中字符串“book”的出现。

import java.util.regex.*;  
class RegexExample1{  
   public static void main(String args[]){  
      String content = "This is Chaitanya " +
	    "from Beginnersbook.com.";

      String pattern = ".*book.*";

      boolean isMatch = Pattern.matches(pattern, content);
      System.out.println("The text contains 'book'? " + isMatch);
   }
}  

输出:

The text contains 'book'? true

在本教程中,我们将学习如何定义模式以及如何使用它们。java.util.regex API(我们在处理 Regex 时需要导入的包)有两个主要类:

1)java.util.regex.Pattern - 用于定义模式

2)java.util.regex.Matcher - 用于使用模式对文本执行匹配操作

java.util.regex.Pattern类:

1)Pattern.matches()

我们已经在上面的例子中看到过这种方法的用法,我们在给定的文本中搜索了字符串"book"。这是使用 Regex 在文本中搜索String的最简单和最简单的方法之一。

String content = "This is a tutorial Website!";
String patternString = ".*tutorial.*";
boolean isMatch = Pattern.matches(patternString, content);
System.out.println("The text contains 'tutorial'? " + isMatch);

如您所见,我们使用Pattern类的matches()方法来搜索给定文本中的模式。模式.*tutorial.*在字符串"tutorial"的开头和结尾允许零个或多个字符(表达式.*用于零个或多个字符)。

限制:通过这种方式,我们可以搜索文本中单个出现的模式。要匹配多次出现,您应该使用Pattern.compile()方法(在下一节中讨论)。

2)Pattern.compile()

在上面的例子中,我们在文本中搜索了一个字符串"tutorial",这是一个区分大小写的搜索,但是如果你想进行大小写不敏感搜索或者想要多次搜索,那么你可能需要在用文本搜索之前,先使用Pattern.compile()。这就是这种方法可以用于这种情况的方法。

String content = "This is a tutorial Website!";
String patternString = ".*tuToRiAl.";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);

这里我们使用了一个标志Pattern.CASE_INSENSITIVE来进行不区分大小写的搜索,还有其他几个标志可以用于不同的目的。要阅读有关此类标志的更多信息,请参阅此文档

现在:我们已经获得了一个Pattern实例,但是如何匹配呢?为此,我们需要一个Matcher实例,我们可以使用Pattern.matcher()方法。让我们讨论一下。

3)Pattern.matcher()方法

在上一节中,我们学习了如何使用compile()方法获取Pattern实例。在这里,我们将学习如何使用matcher()方法从Pattern实例获取Matcher实例。

String content = "This is a tutorial Website!";
String patternString = ".*tuToRiAl.*";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);
boolean isMatched = matcher.matches();
System.out.println("Is it a Match?" + isMatched);

输出:

Is it a Match?true

4)Pattern.split()

要根据分隔符将文本拆分为多个字符串(这里使用正则表达式指定分隔符),我们可以使用Pattern.split()方法。这是如何做到的。

import java.util.regex.*;  
class RegexExample2{  
public static void main(String args[]){  
	String text = "ThisIsChaitanya.ItISMyWebsite";
    // Pattern for delimiter
	String patternString = "is";
	Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
	String[] myStrings = pattern.split(text);
	for(String temp: myStrings){
	    System.out.println(temp);
	}
	System.out.println("Number of split strings: "+myStrings.length);
}}

输出:

Th

Chaitanya.It
MyWebsite
Number of split strings: 4

第二个拆分字符串在输出中为null

java.util.regex.Matcher

我们已经讨论过上面的Matcher类了。让我们回忆一下:

创建Matcher实例

String content = "Some text";
String patternString = ".*somestring.*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(content);

主要方法

matches():它在创建 Matcher 实例时将正则表达式与传递给Pattern.matcher()方法的整个文本进行匹配。

...
Matcher matcher = pattern.matcher(content);
boolean isMatch = matcher.matches();

lookingAt():类似于matches()方法,只是它匹配正则表达式只对着文本的开头,而matches()搜索整个文本。

find():搜索文本中正则表达式的出现次数。主要用于搜索多次出现的情况。

start()end():这两种方法通常与find()方法一起使用。它们用于获取使用find()方法找到的匹配项的开始和结束索引。

让我们举个例子使用Matcher方法来找出多次出现:

package beginnersbook.com;
import java.util.regex.*;  
class RegexExampleMatcher{  
public static void main(String args[]){  
  String content = "ZZZ AA PP AA QQQ AAA ZZ";

  String string = "AA";
  Pattern pattern = Pattern.compile(string);
  Matcher matcher = pattern.matcher(content);

  while(matcher.find()) {
     System.out.println("Found at: "+ matcher.start()
    		+ 
    		" - " + matcher.end());
  }
}
}

输出:

Found at: 4 - 6
Found at: 10 - 12
Found at: 17 - 19

现在我们熟悉PatternMatcher类以及将正则表达式与文本匹配的过程。让我们看一下我们定义正则表达式的各种选项:

1)字符串字面值

假设您只想在文本中搜索特定字符串,例如“abc”然后我们可以简单地编写这样的代码:这里的文本和正则表达式都是相同的。
Pattern.matches("abc", "abc")

2)字符类

字符类将输入文本中的单个字符与字符类中的多个允许字符进行匹配。例如[Cc]haitanya将匹配所有出现的字符串"chaitanya",带有小写或大写C。更多例子:
Pattern.matches("[pqr]", "abcd");它会给出错误,因为文中没有pqr
Pattern.matches("[pqr]", "r");当找到r时返回true
Pattern.matches("[pqr]", "pq");返回false,因为其中任何一个都可以在文本中不是两个。

以下是各种字符类构造的完整列表:
[abc]:如果文本中包含其中一个(abc)且只有一次,它将与文本匹配。
[^abc]:除abc以外的任何单个字符(^表示否定)
[a-zA-Z]az,或AZ,包括(范围)
[a-d[m-p]]ad,或mp[a-dm-p](联合)
[a-z&&[def]]:它们中的任何一个(def
[a-z&&[^bc]]az,除了bc[ad-z](减法)
[a-z&&[^m-p]]az,除了mp[a-lq-z](减法)

预定义字符类 - 元字符

这些就像在编写正则表达式时可以使用的短代码。

Construct	Description
.   ->	Any character (may or may not match line terminators)
\d  ->	A digit: [0-9]
\D  ->	A non-digit: [^0-9]
\s  ->	A whitespace character: [ \t\n\x0B\f\r]
\S  ->	A non-whitespace character: [^\s]
\w  ->	A word character: [a-zA-Z_0-9]
\W  ->	A non-word character: [^\w]

对于例如
Pattern.matches("\\d", "1");将返回true
Pattern.matches("\\D", "z");返回true
Pattern.matches(".p", "qp");返回true,点(.)代表任何字符

边界匹配器

^	Matches the beginning of a line.
$	Matches then end of a line.
\b	Matches a word boundary.
\B	Matches a non-word boundary.
\A	Matches the beginning of the input text.
\G	Matches the end of the previous match
\Z	Matches the end of the input text except the final terminator if any.
\z	Matches the end of the input text.

例如
Pattern.matches("^Hello$", "Hello"):返回true,以Hello开始和结束
Pattern.matches("^Hello$", "Namaste! Hello"):返回false,不以Hello开始
Pattern.matches("^Hello$", "Hello Namaste!"):返回false,不以Hello结束

量词

Greedy	Reluctant	Possessive	Matches
X?	X??	X?+	Matches X once, or not at all (0 or 1 time).
X*	X*?	X*+	Matches X zero or more times.
X+	X+?	X++	Matches X one or more times.
X{n}	X{n}?	X{n}+	Matches X exactly n times.
X{n,}	X{n,}?	X{n,}+	Matches X at least n times.
X{n, m)	X{n, m)? X{n, m)+	Matches X at least n time, but at most m times.

几个例子

import java.util.regex.*;  
class RegexExample{  
public static void main(String args[]){  
   // It would return true if string matches exactly "tom"
   System.out.println(
     Pattern.matches("tom", "Tom")); //False

   /* returns true if the string matches exactly 
    * "tom" or "Tom"
    */
   System.out.println(
     Pattern.matches("[Tt]om", "Tom")); //True
   System.out.println(
     Pattern.matches("[Tt]om", "Tom")); //True

   /* Returns true if the string matches exactly "tim" 
    * or "Tim" or "jin" or "Jin"
    */
   System.out.println(
     Pattern.matches("[tT]im|[jJ]in", "Tim"));//True
   System.out.println(
     Pattern.matches("[tT]im|[jJ]in", "jin"));//True

   /* returns true if the string contains "abc" at 
    * any place
    */
   System.out.println(
     Pattern.matches(".*abc.*", "deabcpq"));//True

   /* returns true if the string does not have a 
    * number at the beginning
    */
   System.out.println(
     Pattern.matches("^[^\\d].*", "123abc")); //False
   System.out.println(
     Pattern.matches("^[^\\d].*", "abc123")); //True

   // returns true if the string contains of three letters
   System.out.println(
     Pattern.matches("[a-zA-Z][a-zA-Z][a-zA-Z]", "aPz"));//True
   System.out.println(
     Pattern.matches("[a-zA-Z][a-zA-Z][a-zA-Z]", "aAA"));//True
   System.out.println(
     Pattern.matches("[a-zA-Z][a-zA-Z][a-zA-Z]", "apZx"));//False

   // returns true if the string contains 0 or more non-digits
   System.out.println(
     Pattern.matches("\\D*", "abcde")); //True
   System.out.println(
     Pattern.matches("\\D*", "abcde123")); //False

   /* Boundary Matchers example
    * ^ denotes start of the line
    * $ denotes end of the line
    */
   System.out.println(
     Pattern.matches("^This$", "This is Chaitanya")); //False
   System.out.println(
     Pattern.matches("^This$", "This")); //True
   System.out.println(
     Pattern.matches("^This$", "Is This Chaitanya")); //False
}
}

参考

正则表达式 - Javadoc

如何编译和运行您的第一个 Java 程序

原文: https://beginnersbook.com/2013/05/first-java-program/

在本教程中,我们将了解如何编写,编译和运行 java 程序。我还将介绍 java 语法,代码约定以及运行 java 程序的几种方法。

简单的 Java 程序:

public class FirstJavaProgram {
  public static void main(String[] args){
    System.out.println("This is my first program in java");
  }//End of main
}//End of FirstJavaProgram Class

输出:这是我在 java 中的第一个程序

如何编译和运行上面的程序

先决条件:你需要在你的系统上安装 java。你可以从这里获取 java。

第 1 步:打开一个文本编辑器,如 Windows 上的记事本和 Mac 上的 TextEdit。复制上述程序并将其粘贴到文本编辑器中。

您也可以使用像 Eclipse 这样的 IDE 来运行 java 程序,但我们将在后面的教程中介绍该部分。为简单起见,本教程仅使用文本编辑器和命令提示符(或终端)

第 2 步:将文件保存为 FirstJavaProgram.java 。您可能想知道为什么我们将文件命名为FirstJavaProgram,我们应该始终将文件命名为public class名称。在我们的程序中,public class名称是FirstJavaProgram,这就是为什么我们的文件名应该是 FirstJavaProgram.java

第 3 步:在这一步中,我们将编译程序。为此,在 Windows 上打开命令提示符(cmd),如果您是 Mac OS,则打开终端
要编译程序,请键入以下命令并按 Enter 键。

javac FirstJavaProgram.java

尝试编译程序时可能会出现此错误:“ javac未被识别为内部或外部命令,可运行程序或批处理文件”。在系统中未设置 java 路径时会发生此错误

如果出现此错误,则首先需要在编译之前设置路径。

在 Windows 中设置路径:

打开命令提示符(cmd),转到系统上已安装 java 的位置,找到 bin 目录,复制完整路径并在命令中写入这个。

set path=C:\Program Files\Java\jdk1.8.0_121\bin

注意:您的 jdk 版本可能会有所不同。由于我的系统上安装了 java 版本1.8.0_121,因此我在设置路径时也提到了相同的内容。

在 Mac OS X

打开终端中设置路径,输入以下命令并按回车键。

export JAVA_HOME=/Library/Java/Home

在终端上键入以下命令以确认路径。

echo $JAVA_HOME

而已。

上面的步骤用于设置临时路径,这意味着当您关闭命令提示符或终端时,路径设置将丢失,您必须在下次使用它时再次设置路径。我将在下一个教程中分享永久路径设置指南。

第 4 步:编译完成后,.java文件被转换为.class文件(字节码)。现在我们可以运行该程序。要运行该程序,请键入以下命令并按Enter键:

java FirstJavaProgram

请注意,在运行程序时,不应将.java扩展名附加到文件名。

仔细看看第一个 Java 程序

现在我们已经了解了如何运行 java 程序,让我们仔细看看上面编写的程序。

public class FirstJavaProgram {

这是我们 java 程序的第一行。每个 java 应用必须至少有一个类定义,包含class关键字后跟类名。当我说关键字时,它意味着它不应该被改变,我们应该按原样使用它。但是类名可以是任何名称。

我已经通过使用公共访问修饰符公开了类,我将在一个单独的帖子中介绍访问修饰符,现在你需要知道一个 java 文件可以有任意数量的类但它只能有一个公共类和文件名应该与公共类名相同。

public static void main(String[] args)  {

这是我们在程序中的下一行,让我们分解它来理解它:
public:这使得main方法公开,这意味着我们可以从类外调用该方法。

static:我们不需要为静态方法创建对象来运行。他们可以自己跑。

void:它没有返回任何东西。

main:这是方法名称。这是 JVM 可以运行程序的入口点方法。

(String[] args):用于作为字符串传递的命令行参数。我们将在另一篇文章中介绍。

System.out.println("This is my first program in java");

此方法将双引号内的内容打印到控制台中,然后插入换行符。

在阅读下一个主题之前,查看这些基本的 java 程序

其它核心 Java 教程

Java - String类及其方法

原文: https://beginnersbook.com/2013/12/java-strings/

字符串是一系列字符,例如"Hello"是一个包含 5 个字符的字符串。在 java 中,字符串是一个不可变对象,这意味着它是常量,一旦创建就无法更改。在本教程中,我们将详细了解String类和String方法以及许多其他 Java String教程。

创建一个字符串

有两种方法可以在 Java 中创建String

  1. 字符串字面量
  2. 使用new关键字

字符串字面量

在 java 中,可以像这样创建字符串:将字符串字面值分配给String实例:

String str1 = "Welcome";
String str2 = "Welcome";

这种方法的问题:正如我在开头所说的那样,String是 Java 中的一个对象。但是我们没有使用上面的new关键字创建任何字符串对象。编译器为我们执行该任务它创建了一个字符串对象,该字符串对象具有字符串字面值(我们已经提供,在这种情况下它是"Welcome")并将其分配给提供的字符串实例。

但是如果对象已经存在于内存中它不会创建新的对象,而是将同一个旧对象分配给新实例,这意味着即使我们上面有两个字符串实例(str1str2)编译器仅创建一个(具有值"Welcome")并将其分配给两个实例。例如,有 10 个字符串实例具有相同的值,这意味着在内存中只有一个对象具有该值,并且所有 10 个字符串实例将指向同一个对象。

如果我们想要两个具有相同字符串的不同对象,该怎么办?为此,我们需要使用new关键字创建字符串。

使用new关键字

正如我们在上面看到的那样,当我们尝试将相同的字符串对象分配给两个不同的文字时,编译器只创建了一个对象并使两个文字都指向同一个对象。为了克服这种方法,我们可以创建这样的字符串:

String str1 = new String("Welcome");
String str2 = new String("Welcome");

在这种情况下,编译器将在内存中创建具有相同字符串的两个不同对象。

一个简单的 Java 字符串示例

public class Example{  
   public static void main(String args[]){  
	//creating a string by java string literal 
	String str = "Beginnersbook"; 
	char arrch[]={'h','e','l','l','o'}; 
	//converting char array arrch[] to string str2
	String str2 = new String(arrch); 

	//creating another java string str3 by using new keyword 
	String str3 = new String("Java String Example"); 

	//Displaying all the three strings
	System.out.println(str);  
	System.out.println(str2);  
	System.out.println(str3);  
   }
}

输出:

Beginnersbook
hello
Java String Example

Java 字符串方法

以下是 Java String类中可用方法的列表。在示例的帮助下,在单独的教程中解释了这些方法。下面提供了教程的链接:

  1. char charAt(int index):返回指定索引处的字符。指定的索引值应介于 0 到length()-1之间。如果index < 0 || >=字符串的长度,它会抛出IndexOutOfBoundsException
  2. boolean equals(Object obj):将字符串与指定的字符串进行比较,如果两者匹配,则返回true
  3. boolean equalsIgnoreCase(String string):它的作用与 equals 方法相同,但在比较字符串时不考虑这种情况。它做了一个不区分大小写的比较。
  4. int compareTo(String string):此方法根据字符串中每个字符的 Unicode 值比较两个字符串。
  5. int compareToIgnoreCase(String string):与CompareTo方法相同但是它在比较期间忽略了这种情况。
  6. boolean startsWith(String prefix, int offset):它检查子字符串(从指定的偏移索引开始)是否具有指定的前缀。
  7. boolean startsWith(String prefix):它测试字符串是否具有指定的前缀,如果是,则返回true,否则返回false
  8. boolean endsWith(String suffix):检查字符串是否以指定的后缀结尾。
  9. int hashCode():返回字符串的哈希码。
  10. int indexOf(int ch):返回字符串中第一次出现指定字符ch的索引。
  11. int indexOf(int ch, int fromIndex):与indexOf方法相同,但是它从指定的fromIndex开始搜索字符串。
  12. int lastIndexOf(int ch):返回字符串中字符ch的最后一次出现。
  13. int lastIndexOf(int ch, int fromIndex):与lastIndexOf(int ch)方法相同,它从fromIndex开始搜索。
  14. int indexOf(String str):此方法返回指定子串str的第一次出现的索引。
  15. int lastindexOf(String str):返回字符串str的最后一次出现的的索引。
  16. String substring(int beginIndex):返回字符串的子串。子字符串以指定索引处的字符开头。
  17. String substring(int beginIndex, int endIndex):返回子字符串。子字符串以beginIndex中的字符开头,以endIndex中的字符结束。
  18. String concat(String str):在字符串末尾连接指定的字符串str
  19. String replace(char oldChar, char newChar):在使用newChar更改oldChar的所有出现后,它返回更新的字符串。
  20. boolean contains(CharSequence s):它检查字符串是否包含指定的char值序列。如果是,则返回true,否则返回false。如果snull,它抛出 NullPointerException`。
  21. String toUpperCase(Locale locale):使用指定语言环境定义的规则将字符串转换为大写字符串。
  22. String toUpperCase():相当于toUpperCase(Locale.getDefault())
  23. public String intern():此方法搜索内存池中的指定字符串,如果找到它,则返回它的引用,否则它将内存空间分配给指定的字符串,并将引用分配给它。
  24. public boolean isEmpty():如果给定的字符串长度为 0,则此方法返回true。如果指定的 Java 字符串的长度不为零,则返回false
  25. public static String join():此方法使用指定的分隔符连接给定的字符串并返回连接的 Java 字符串
  26. String replaceFirst(String regex, String replacement):它用指定的替换字符串替换第一次出现的,符合给定正则表达式regex的子字符串。
  27. String replaceAll(String regex, String replacement):它用replacement字符串替换所有适合正则表达式regex的子串。
  28. String[] split(String regex, int limit):它拆分字符串并返回与给定正则表达式匹配的子字符串数组。 limit 是这里的结果阈值。
  29. String[] split(String regex):与 split(String regex, int limit)方法相同,但它没有任何阈值限制。
  30. String toLowerCase(Locale locale):它使用给定语言环境定义的规则将字符串转换为小写字符串。
  31. public static String format():此方法返回格式化的 java 字符串
  32. String toLowerCase():相当于toLowerCase(Locale。getDefault())
  33. String trim():从原始字符串中省略前导和尾随空格后返回子字符串。
  34. char[] toCharArray():将字符串转换为字符数组。
  35. static String copyValueOf(char[] data):返回包含指定字符数组字符的字符串。
  36. static String copyValueOf(char[] data, int offset, int count):与上面的方法相同,带有两个额外的参数 - 子数组的初始偏移量和子数组的长度。
  37. void getChars(int srcBegin, int srcEnd, char [] dest, int destBegin):它将src数组的字符复制到dest数组。只有指定的范围被复制(srcBeginsrcEnd)到dest子数组(从fromBegin开始)。
  38. static String valueOf():此方法返回传递参数的字符串表示形式,如intlongfloatdoublecharchar数组。
  39. boolean contentEquals(StringBuffer sb):它将字符串与指定的字符串缓冲区进行比较。
  40. boolean regionMatches(int srcoffset, String dest, int destoffset, int len):它将输入的子字符串与指定字符串的子字符串进行比较。
  41. boolean regionMatches(boolean ignoreCase, int srcoffset, String dest, int destoffset, int len):regionMatches 方法的另一种变体,带有额外的布尔参数,用于指定比较是区分大小写还是不区分大小写。
  42. byte[] getBytes(String charsetName):使用指定的charset编码将字符串转换为字节序列,并返回结果字节数组。
  43. byte[] getBytes():此方法类似于上面的方法,它只使用默认的字符集编码将字符串转换为字节序列。
  44. int length():返回字符串的长度。
  45. boolean matches(String regex):它检查字符串是否与指定的正则表达式匹配。
  46. int codePointAt(int index):它类似于 charAt 方法,但它返回指定索引的 Unicode 代码点值而不是字符本身。

流行的 Java 字符串教程

我在 java 中分享了几个关于String的教程。以下是流行的 Java String教程的链接。

  1. Stringint的转换
  2. intString的转换
  3. StringDouble的转换
  4. DoubleString的转换
  5. Stringlong的转换
  6. longString的转换
  7. InputStreamString的转换
  8. String vs StringBuffer
  9. Stringboolean的转换
  10. booleanString的转换
  11. StringBoolean的转换
  12. 删除字符串的尾随空格
  13. 使用零/空格左填充字符串
  14. 使用零/空格右填充字符串
  15. 在字符串中查找重复字符的程序
  16. charString的转换,以及相反
  17. char数组到String的转换
  18. StringDate的转换
  19. DateString的转换
  20. ASCII 到String的转换
  21. floatString的转换
  22. StackTraceString的转换
  23. WriterString的转换
  24. StringArrayList的转换
  25. Java 8 - StringJoiner示例
  26. 反转String的单词的 Java 程序
  27. 使用递归反转String的 Java 程序

参考文献:

java 多线程

原文: https://beginnersbook.com/2013/03/multithreading-in-java/

在我们讨论多线程之前,让我们讨论一下线程。线程是进程的轻量级最小部分,可以与同一进程的其他部分(其他线程)同时运行。线程是独立的,因为它们都有单独的执行路径,这是在一个线程中发生异常的原因,它不会影响其他线程的执行。进程的所有线程共享公共内存。 同时执行多个线程的过程称为多线程。

让我们总结讨论点:

  1. 多线程的主要目的是同时执行程序的两个或多个部分,以最大限度地利用 CPU 时间。多线程程序包含两个或多个可以并发运行的部分。程序的每个这样的部分都称为线程。

  2. 线程是轻量级子进程,它们共享公共内存空间。在多线程环境中,受益于多线程的程序利用最大 CPU 时间,以便将空闲时间保持在最小。

  3. 线程可以处于以下状态之一:
    NEW - 尚未启动的线程处于此状态。
    RUNNABLE - 在 Java 虚拟机中执行的线程处于此状态。
    BLOCKED - 等待监视器锁定被阻塞的线程处于此状态。
    WAITING - 一个无限期等待另一个线程执行特定操作的线程处于此状态。
    TIMED_WAITING - 正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
    TERMINATED - 已退出的线程处于此状态。
    线程在给定时间点只能处于一种状态。

在此链接中了解有关线程状态的更多信息线程的生命周期

多任务与多线程与多处理与并行处理

如果您是 java 新手,您可能会对这些术语感到困惑,因为在我们讨论多线程时它们会被频繁使用。我们简要地谈谈它们。

多任务处理:能够同时执行多个任务称为多任务处理。

多线程:我们已经讨论过了。这是一个同时执行多个线程的过程。多线程也称为基于线程的多任务处理。

多处理:它与多任务处理相同,但在多处理中涉及多个 CPU。另一方面,一个 CPU 涉及多任务处理。

并行处理:它指的是在单个计算机系统中使用多个 CPU。

用 Java 创建一个线程

在 Java 中创建线程有两种方法:

1)通过扩展Thread类。

2)通过实现Runnable接口。

在开始创建线程的程序(代码)之前,让我们看一下Thread类的这些方法。我们在下面的示例中使用了很少的这些方法。

  • getName():用于获取线程的名称
  • getPriority():获取线程的优先级
  • isAlive():确定线程是否仍在运行
  • join():等待线程终止
  • run():线程的入口点
  • sleep():暂停线程一段时间
  • start():通过调用run()方法启动一个线程

方法 1:通过扩展Thread类创建线程

例 1:

class MultithreadingDemo extends Thread{  
  public void run(){  
    System.out.println("My thread is in running state.");  
  }   
  public static void main(String args[]){  
     MultithreadingDemo obj=new MultithreadingDemo();   
     obj.start();  
  }  
}

输出:

My thread is in running state.

例 2:

class Count extends Thread
{
   Count()
   {
     super("my extending thread");
     System.out.println("my thread created" + this);
     start();
   }
   public void run()
   {
     try
     {
        for (int i=0 ;i<10;i++)
        {
           System.out.println("Printing the count " + i);
           Thread.sleep(1000);
        }
     }
     catch(InterruptedException e)
     {
        System.out.println("my thread interrupted");
     }
     System.out.println("My thread run is over" );
   }
}
class ExtendingExample
{
   public static void main(String args[])
   {
      Count cnt = new Count();
      try
      {
         while(cnt.isAlive())
         {
           System.out.println("Main thread will be alive till the child thread is live");
           Thread.sleep(1500);
         }
      }
      catch(InterruptedException e)
      {
        System.out.println("Main thread interrupted");
      }
      System.out.println("Main thread's run is over" );
   }
}

输出:

my thread createdThread[my runnable thread,5,main]
Main thread will be alive till the child thread is live
Printing the count 0
Printing the count 1
Main thread will be alive till the child thread is live
Printing the count 2
Main thread will be alive till the child thread is live
Printing the count 3
Printing the count 4
Main thread will be alive till the child thread is live
Printing the count 5
Main thread will be alive till the child thread is live
Printing the count 6
Printing the count 7
Main thread will be alive till the child thread is live
Printing the count 8
Main thread will be alive till the child thread is live
Printing the count 9
mythread run is over
Main thread run is over

方法 2:通过实现Runnable接口创建线程

一个简单的例子

class MultithreadingDemo implements Runnable{  
  public void run(){  
    System.out.println("My thread is in running state.");  
  }   
  public static void main(String args[]){  
     MultithreadingDemo obj=new MultithreadingDemo();  
     Thread tobj =new Thread(obj);  
     tobj.start();  
 }  
}

输出:

My thread is in running state.

示例程序 2:

观察该程序的输出并尝试了解该程序中发生的情况。如果你已经理解了每个线程方法的用法,那么你应该不会遇到任何问题,理解这个例子。

class Count implements Runnable
{
   Thread mythread ;
   Count()
   { 
      mythread = new Thread(this, "my runnable thread");
      System.out.println("my thread created" + mythread);
      mythread.start();
   }
   public void run()
   {
      try
      {
        for (int i=0 ;i<10;i++)
        {
          System.out.println("Printing the count " + i);
          Thread.sleep(1000);
        }
     }
     catch(InterruptedException e)
     {
        System.out.println("my thread interrupted");
     }
     System.out.println("mythread run is over" );
   }
}
class RunnableExample
{
    public static void main(String args[])
    {
       Count cnt = new Count();
       try
       {
          while(cnt.mythread.isAlive())
          {
            System.out.println("Main thread will be alive till the child thread is live"); 
            Thread.sleep(1500);
          }
       }
       catch(InterruptedException e)
       {
          System.out.println("Main thread interrupted");
       }
       System.out.println("Main thread run is over" );
    }
}

输出:

my thread createdThread[my runnable thread,5,main]
Main thread will be alive till the child thread is live
Printing the count 0
Printing the count 1
Main thread will be alive till the child thread is live
Printing the count 2
Main thread will be alive till the child thread is live
Printing the count 3
Printing the count 4
Main thread will be alive till the child thread is live
Printing the count 5
Main thread will be alive till the child thread is live
Printing the count 6
Printing the count 7
Main thread will be alive till the child thread is live
Printing the count 8
Main thread will be alive till the child thread is live
Printing the count 9
mythread run is over
Main thread run is over

线程优先级

  • 线程优先级是整数,它决定了如何相对于其他线程处理一个线程。
  • 线程优先级决定何时从一个正在运行的线程切换到另一个线程,该进程称为上下文切换
  • 线程可以自动释放控制,并且准备运行的最高优先级线程被赋予 CPU。
  • 无论优先级较低的线程正在做什么,线程都可以被更高优先级的线程抢占。每当一个更高优先级的线程想要运行它。
  • 要设置线程的优先级setPriority()方法,这是类Thread类的方法。
  • 我们可以使用MIN_PRIORITYNORM_PRIORITYMAX_PRIORITY代替以整数定义优先级。

方法:isAlive()join()

  • 在所有实际情况中,主线程应该最后完成其他从主线程产生的其他线程也将完成。
  • 要知道线程是否已完成,我们可以在线程上调用isAlive(),如果线程未完成则返回true
  • 另一种通过使用join()方法实现此目的的方法,从父线程调用此方法使父线程等待直到子线程终止。
  • 这些方法在Thread类中定义。
  • 我们在上面的例子中也使用了isAlive()方法。

同步

  • 多线程为程序引入了异步行为。如果一个线程正在写一些数据,那么另一个线程可能正在读取相同的数据。这可能会带来不一致。
  • 当两个或多个线程需要访问共享资源时,应该有某种方式资源一次只能由一个资源使用。实现此目的的过程称为同步。
  • 要实现同步行为,java 具有同步方法。一旦线程在同步方法中,任何其他线程都不能在同一对象上调用任何其他同步方法。然后所有其他线程一直等到第一个线程从同步块中出来。
  • 当我们想要同步访问不是为多线程访问而设计的类的对象时,我们无法获得需要同步访问的方法的代码,在这种情况下,我们无法将synchronized添加到适当的方法中。在 java 中我们有这个解决方案,以下列方式调用此类在同步块内定义的方法(需要同步)。
Synchronized(object)
{
    // statement to be synchronized
}

线程间通信

我们有很少的方法可以让 java 线程相互通信。这些方法是wait()notify()notifyAll()。所有这些方法只能在同步方法中调用。

1)了解同步 java 有一个监视器的概念。监视器可以被认为是一个只能容纳一个线程的盒子。一旦线程进入监视器,所有其他线程必须等待该线程退出监视器。

2)wait()告诉调用线程放弃监视器并进入休眠状态,直到某个其他线程进入同一个监视器并调用notify()

3)notify()唤醒在同一个对象上调用wait()的第一个线程。
notifyAll()唤醒在同一个对象上调用wait()的所有线程。优先级最高的线程将首先运行。

参考文献

  • Herbert Schildt 撰写的 Java 2 完整参考

Java 序列化

原文: https://beginnersbook.com/2014/07/java-serialization/

在这里,我们将讨论如何序列化和反序列化对象以及它的用途。

什么是 Java 序列化?

序列化是一种将对象转换为字节流的机制,以便可以将其写入文件,通过网络传输或存储到数据库中。反序列化反之亦然。简单来说,序列化是将对象转换为字节流,反序列化是从字节流重建对象。 Java 序列化 API 执行序列化和反序列化。类必须实现java.io.Serializable接口才有资格进行序列化。

让我们举个例子来更好地理解这些概念:

示例

此类实现Serializable接口,这意味着它可以被序列化。除了那些声明为transient的字段外,该类的所有字段都可以在转换为字节流后写入文件。在下面的示例中,我们有两个瞬态字段,这些字段不参与序列化。

Student.java

public class Student implements java.io.Serializable{
  private int stuRollNum;
  private int stuAge;
  private String stuName;
  private transient String stuAddress;
  private transient int stuHeight;

  public Student(int roll, int age, String name,
  String address, int height) {
    this.stuRollNum = roll;
    this.stuAge = age;
    this.stuName = name;
    this.stuAddress = address;
    this.stuHeight = height;
  }

  public int getStuRollNum() {
    return stuRollNum;
  }
  public void setStuRollNum(int stuRollNum) {
    this.stuRollNum = stuRollNum;
  }
  public int getStuAge() {
    return stuAge;
  }
  public void setStuAge(int stuAge) {
    this.stuAge = stuAge;
  }
  public String getStuName() {
    return stuName;
  }
  public void setStuName(String stuName) {
    this.stuName = stuName;
  }
  public String getStuAddress() {
    return stuAddress;
  }
  public void setStuAddress(String stuAddress) {
    this.stuAddress = stuAddress;
  }
  public int getStuHeight() {
    return stuHeight;
  }
  public void setStuHeight(int stuHeight) {
    this.stuHeight = stuHeight;
  }
}

对象的序列化

该类正在将Student类的对象写入Student.ser文件。我们使用FileOutputStreamObjectOutputStream将对象写入File

注意:根据 Java 序列化的最佳实践,文件名应具有.ser扩展名。

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
public class SendClass
{
  public static void main(String args[])
  {
    Student obj = new Student(101, 25, "Chaitanya", "Agra", 6);
    try{ 
      FileOutputStream fos = new FileOutputStream("Student.ser"); 
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
      oos.close();
      fos.close();
      System.out.println("Serialzation Done!!");
   }catch(IOException ioe){
      System.out.println(ioe);
    }
  }
}

输出:

Serialzation Done!!

对象的反序列化

从读取文件中的字节流后,该类将重建Student类的对象。观察此课程的输出,学生地址和学生身高字段为null和 0。这是因为这些字段在Student类中被声明为transient

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
public class AcceptClass {

 public static void main(String args[])
 {
    Student o=null;
    try{
      FileInputStream fis = new FileInputStream("Student.ser");
      ObjectInputStream ois = new ObjectInputStream(fis);
      o = (Student)ois.readObject();
      ois.close();
      fis.close();
    }
    catch(IOException ioe)
    {
       ioe.printStackTrace();
       return;
    }catch(ClassNotFoundException cnfe)
     {
       System.out.println("Student Class is not found.");
       cnfe.printStackTrace();
       return;
     }
    System.out.println("Student Name:"+o.getStuName());
    System.out.println("Student Age:"+o.getStuAge());
    System.out.println("Student Roll No:"+o.getStuRollNum());
    System.out.println("Student Address:"+o.getStuAddress());
    System.out.println("Student Height:"+o.getStuHeight());
 }
}

输出:

Student Name:Chaitanya
Student Age:25
Student Roll No:101
Student Address:null
Student Height:0

Java AWT 初学者教程

原文: https://beginnersbook.com/2015/06/java-awt-tutorial/

AWT 代表抽象窗口工具包。它是一个依赖于平台的 API,用于为 Java 程序创建图形用户界面(GUI)。

为什么 AWT 与平台有关? Java AWT 调用本机平台(操作系统)子程序,用于创建文本框,复选框,按钮等组件。例如,具有按钮的 AWT GUI 在诸如 Windows,Mac OS 和 Windows 等平台之间具有不同的外观和感觉。 Unix,这是因为这些平台的原生按钮具有不同的外观和感觉,AWT 直接调用创建按钮的原生子程序。简单来说,基于 AWT 的应用在 Windows 上运行时看起来就像一个 Windows 应用,但在 Mac OS 上运行时,相同的应用看起来就像是 Mac 应用。

AWT 现在很少使用,因为它具有平台依赖性和重量级特性。 AWT 组件被认为是重量级的,因为它们是由底层操作系统(OS)生成的。例如,如果您在 AWT 中实例化一个文本框,这意味着您实际上要求操作系统为您创建一个文本框。

Swing 是基于窗口的应用的首选 API,因为它具有平台独立性和轻量级特性。 Swing 基于 AWT API 构建,但它提供与底层平台无关的外观。它具有比 AWT 更强大,更灵活的组件。除了熟悉的组件,如按钮,复选框和标签,Swing 还提供了几个高级组件,如选项卡式面板,滚动窗格,树,表和列表。我们将在单独的教程中详细讨论 Swing。

AWT 层次结构

Java AWT hierarchy diagram

组件和容器

按钮,文本字段,滚动条等所有元素都称为组件。在 AWT 中,我们为每个组件提供了类,如上图所示。要将屏幕上的所有内容放置到特定位置,我们必须将它们添加到容器中。容器就像一个屏幕,我们在其中放置按钮,文本字段,复选框等组件。简而言之,容器包含并控制组件的布局。容器本身是一个组件(如上面的层次结构图所示),因此我们可以在容器内添加一个容器。

容器类型:

如上所述,容器是我们添加文本字段,按钮,复选框等组件的地方。AWT 中有四种类型的容器:窗口,框架,对话框和小组。如上面的层次结构图所示,FrameDialogWindow类的子类。

窗口: Window类的一个实例没有边框也没有标题
对话框: Dialog类有边框和标题。没有Frame类的关联实例,Dialog类的实例不能存在。
面板:面板不包含标题栏,菜单栏或边框。它是用于保持组件的通用容器。Panel类的实例提供了一个要添加组件的容器。
框架:框架有标题,边框和菜单栏。它可以包含几个组件,如按钮,文本字段,滚动条等。这是在 AWT 中开发应用时使用最广泛的容器。

Java AWT 示例

我们可以用两种方式使用Frame创建一个 GUI:

1)通过扩展Frame

2)通过创建Frame类的实例
让我们看一下每个示例。

AWT 示例 1:通过扩展Frame类来创建Frame

import java.awt.*;
/* We have extended the Frame class here,
 * thus our class "SimpleExample" would behave
 * like a Frame
 */
public class SimpleExample extends Frame{
    SimpleExample(){  
        Button b=new Button("Button!!"); 

        // setting button position on screen
        b.setBounds(50,50,50,50);  

        //adding button into frame 
        add(b); 

        //Setting Frame width and height
        setSize(500,300); 

        //Setting the title of Frame
        setTitle("This is my First AWT example"); 

        //Setting the layout for the Frame
        setLayout(new FlowLayout());

        /* By default frame is not visible so 
         * we are setting the visibility to true 
         * to make it visible.
         */
        setVisible(true);  
    }  
    public static void main(String args[]){  
         // Creating the instance of Frame
         SimpleExample fr=new SimpleExample();  
    }
}

输出:

AWT example 1

AWT 示例 2:通过创建Frame类的实例来创建Frame

import java.awt.*;
public class Example2 {
   Example2()
   {
      //Creating Frame    
      Frame fr=new Frame();       

      //Creating a label
      Label lb = new Label("UserId: "); 

      //adding label to the frame
      fr.add(lb);           

      //Creating Text Field
      TextField t = new TextField();

      //adding text field to the frame
      fr.add(t);

      //setting frame size
      fr.setSize(500, 300);  

      //Setting the layout for the Frame
      fr.setLayout(new FlowLayout());

      fr.setVisible(true);                
   }
   public static void main(String args[])
   {
       Example2 ex = new Example2(); 
   }
}

输出:

AWT example 2

适合初学者的 Java Swing 教程

原文: https://beginnersbook.com/2015/07/java-swing-tutorial/

Swing 是 Java 基础类(JFC)的一部分,JFC 的其他部分是 java2D 和抽象 window 工具包(AWT)。 AWT,Swing 和 Java 2D 用于在 java 中构建图形用户界面(GUI)。在本教程中,我们将主要讨论用于在 AWT 顶部构建 GUI 的 Swing API,与 AWT 相比,它更轻量级。

一个简单的例子

在下面的示例中,我们将使用您在本教程中到目前为止尚未学习的几个 swing 组件。我们将在即将到来的摇摆教程中详细讨论每一个和所有内容。
下面的 swing 程序会创建一个登录界面。

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField; 
public class SwingFirstExample {

    public static void main(String[] args) {    
        // Creating instance of JFrame
        JFrame frame = new JFrame("My First Swing Example");
        // Setting the width and height of frame
        frame.setSize(350, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        /* Creating panel. This is same as a div tag in HTML
         * We can create several panels and add them to specific 
         * positions in a JFrame. Inside panels we can add text 
         * fields, buttons and other components.
         */
        JPanel panel = new JPanel();    
        // adding panel to frame
        frame.add(panel);
        /* calling user defined method for adding components
         * to the panel.
         */
        placeComponents(panel);

        // Setting the frame visibility to true
        frame.setVisible(true);
    }

    private static void placeComponents(JPanel panel) {

        /* We will discuss about layouts in the later sections
         * of this tutorial. For now we are setting the layout 
         * to null
         */
        panel.setLayout(null);

        // Creating JLabel
        JLabel userLabel = new JLabel("User");
        /* This method specifies the location and size
         * of component. setBounds(x, y, width, height)
         * here (x,y) are cordinates from the top left 
         * corner and remaining two arguments are the width
         * and height of the component.
         */
        userLabel.setBounds(10,20,80,25);
        panel.add(userLabel);

        /* Creating text field where user is supposed to
         * enter user name.
         */
        JTextField userText = new JTextField(20);
        userText.setBounds(100,20,165,25);
        panel.add(userText);

        // Same process for password label and text field.
        JLabel passwordLabel = new JLabel("Password");
        passwordLabel.setBounds(10,50,80,25);
        panel.add(passwordLabel);

        /*This is similar to text field but it hides the user
         * entered data and displays dots instead to protect
         * the password like we normally see on login screens.
         */
        JPasswordField passwordText = new JPasswordField(20);
        passwordText.setBounds(100,50,165,25);
        panel.add(passwordText);

        // Creating login button
        JButton loginButton = new JButton("login");
        loginButton.setBounds(10, 80, 80, 25);
        panel.add(loginButton);
    }

}

输出:

Swing Login Screen Example

在上面的例子中,我们使用了几个组件。我们先讨论一下它们,然后我们将在下一个教程中详细讨论它们。
JFrame - 帧是JFrame的一个实例。框架是一个窗口,可以有标题,边框,菜单,按钮,文本字段和其他几个组件。 Swing 应用必须有一个框架才能添加组件。
JPanel - 面板是JPanel的一个实例。一个框架可以有多个面板,每个面板可以有几个组件。你也可以称它们为 Frame 的一部分。面板可用于对组件进行分组并将它们放置在框架中的适当位置。

JLabel - 标签是JLabel类的一个实例。标签是不可选择的文本和图像。如果要在框架上显示字符串或图像,可以使用标签。在上面的例子中,我们想要在文本字段之前显示文本"User""Password",我们通过创建标签并将其添加到适当的位置来实现此目的。

JTextField - 用于捕获用户输入,这些是用户输入数据的文本框。

JPasswordField - 与文本字段类似,但输入的数据被隐藏并在 GUI 上显示为点。

JButton - 一个按钮是JButton类的一个实例。在上面的例子中,我们有一个“登录”按钮。

Java 自动装箱和拆箱

原文: https://beginnersbook.com/2014/09/java-autoboxing-and-unboxing-with-examples/

Java 1.5 引入了一种特殊功能,即将原始类型自动转换为相应的包装器类,反之亦然。

自动装箱 :将原始类型自动转换为相应包装类的对象称为自动装箱。例如 - 将int转换为Integer,将long转换为Long,将double转换为Double等。

拆箱:这只是自动装箱的逆过程。自动将包装类的对象转换为其对应的原始类型类型称为拆箱。例如 - 将Integer转换为intLong转换为long,将Double转换为double等。

Primitive type	Wrapper class
boolean	        Boolean
byte	        Byte
char	        Character
float	        Float
int	        Integer
long	        Long
short	        Short
double	        Double

Java 中什么时候发生自动装箱和拆箱

自动装箱

让我们看一些带有示例的案例,其中发生了自动装箱。

案例 1 :当一个方法期望一个包装器类对象但是作为参数传递的值是一个基本类型。例如,在下面的代码中,方法myMethod()期望一个Integer包装类的对象,但是我们传递了一个原始的int类型。程序运行正常,因为编译器执行自动装箱(将int转换为Integer

class AutoboxingExample1
{
   public static void myMethod(Integer num){
	System.out.println(num);
   }
   public static void main(String[] args) {
       /* passed int (primitive type), it would be 
        * converted to Integer object at Runtime
        */
   	myMethod(2);
   }
}

输出:

2

案例 2 :在某个时间点,您将原始类型值分配给其包装类的对象。例如:以下语句有效,因为编译器在运行时执行自动装箱。

Integer inum = 3; //Assigning int to Integer: Autoboxing
Long lnum = 32L; //Assigning long to Long: Autoboxing

案例 3 :处理集合框架类时:

ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(11); //Autoboxing - int primitive to Integer
arrayList.add(22); //Autoboxing

这里ArrayList类需要一个Integer包装类对象,但是我们提供了int原始类型。

拆箱

案例 1 :方法期待Integer对象(参数)但我们提供了int。发生了将Integer转换为int的自动转换(拆箱)。

class UnboxingExample1
{
   public static void myMethod(int num){
	System.out.println(num);
   }
   public static void main(String[] args) {

    	Integer inum = new Integer(100);

        /* passed Integer wrapper class object, it 
         * would be converted to int primitive type 
         * at Runtime
         */
    	myMethod(inum);
    }
}

输出:

100

案例 2 :作业

Integer inum = new Integer(5);
int num = inum; //unboxing object to primitive conversion

案例 3 :在处理集合类时:

ArrayList arrayList = new ArrayList()
int num = arrayList.get(0); // unboxing because get method returns an Integer object

幕后发生了什么?

在上一节中,我们学习了 java 编译器如何在基本类型和相应的包装器对象之间执行自动转换。让我们讨论一下编译器在自动装箱和拆箱过程中实际做了什么。理解这一点的最好方法是比较 java 1.5 之前和 java 1.5 之后的事情(java 1.5 中引入的装箱和拆箱)。

自动装箱

我们看到了什么:

Integer number = 100;

编译器做了什么(或者我们以前在 java 1.5 之前做过的事情):

Integer number = Integer.valueOf(100);

拆箱

我们看到了什么:

Integer num2 = new Integer(50);
int inum = num2;

编译器做什么:

Integer num2 = new Integer(50);
int inum = num2.intValue();

类似的事情发生在其他包装类和原始类型,如longdoubleshort等。

你应该注意的事情很少:

在进行比较时不要混合原始类型和对象。对于这样的比较,您可能会得到不可预测的结果。更好的做法是:将对象与对象进行比较(使用equals()方法)并将原始类型与原始类型进行比较(使用逻辑运算符,如==<等)。

参考

Javadoc - Autoboxing 和 Unboxing

Java 中的包装类

原文: https://beginnersbook.com/2017/09/wrapper-class-in-java/

OOPs 概念指南中,我们了解到面向对象编程都是关于对象的。八种原始数据类型byteshortintlongfloatdoublecharboolean不是对象, 包装器类用于将原始数据类型转换为对象,如intInteger等。让我们采取一个简单的例子来理解为什么我们需要 java 中的包装类。

例如:在 Java 中使用集合时,我们使用泛型来实现类型安全:ArrayList<Integer>而不是这个ArrayList<int>Integerint基本类型的包装类。在这种情况下,我们使用包装类,因为泛型需要对象而不是原始类型。还有其他几个原因你更喜欢包装类而不是原始类型,我们将在本文中讨论它们。

原始 包装类
boolean Boolean
char Char
byte Byte
short Short
int Integer
long Long
float Float
double Double

为什么我们需要 Java 中的包装类

  1. 正如我上面提到的,我们需要包装器的原因之一是在集合 API 中使用它们。另一方面,与基本类型相比,包装器对象拥有更多的内存。因此,当您需要效率时使用原始类型,并在需要对象而不是原始类型时使用包装类。

原始数据类型不是对象,因此它们不属于任何类。存储在仅支持对象的数据结构中时,需要首先将基本类型转换为对象,我们可以通过使用包装类来完成。

例:

HashMap<Integer, String> hm = new HashMap<Integer, String>();

因此,对于类型安全,我们使用包装类。这样我们就可以确保这个HashMap 键是整数类型,值是字符串类型。

  1. 包装类对象允许空值,而原始数据类型不允许空值。

让我们举几个例子来了解转换的工作原理:

包装类示例 1:将基本类型转换为包装器对象

public class JavaExample{  
   public static void main(String args[]){  
	//Converting int primitive into Integer object  
	int num=100;  
	Integer obj=Integer.valueOf(num);  

	System.out.println(num+ " "+ obj);  
   }
}

输出:

100 100

如您所见,原始数据类型和对象具有相同的值。您可以使用obj代替num,无论您需要将num的值作为对象传递。

包装类示例 2:将包装类对象转换为基本对象

public class JavaExample{  
   public static void main(String args[]){  
	//Creating Wrapper class object 
	Integer obj = new Integer(100);  

	//Converting the wrapper object to primitive
	int num = obj.intValue();

	System.out.println(num+ " "+ obj);  
   }
}

输出:

100 100

Java 8 教程

Java Lambda 表达式教程

原文: https://beginnersbook.com/2017/10/java-lambda-expressions-tutorial-with-examples/

Lambda 表达式是 Java 8 中引入的一个新特性。λ 表达式是一个匿名函数。一个没有名称且不属于任何类的函数。 lambda 表达式的概念最初是在 LISP 编程语言中引入的。

Java Lambda 表达式语法

要创建 lambda 表达式,我们在 lambda 运算符->的左侧指定输入参数(如果有的话),并将表达式或语句块放在 lambda 运算符的右侧。例如,lambda 表达式(x, y) -> x + y指定 lambda 表达式接受两个参数xy并返回这些参数的总和。

//Syntax of lambda expression
(parameter_list) -> {function_body}

Lambda 表达式与 Java 中的方法

Java 中的方法(或函数)具有以下主要部分:

  1. 名称
  2. 参数列表
  3. 正文
  4. 返回类型。

Java 中的 lambda 表达式具有以下主要部分:
Lambda 表达式仅具有主体和参数列表

  1. 没有名称 - 函数是匿名的,所以我们不关心名称
  2. 参数列表
  3. 正文 - 这是函数的主要部分。
  4. 没有返回类型 - java 8 编译器能够通过检查代码来推断返回类型。你不必明确提及它。

在 Java 中使用 Lambdas 的位置

要使用 lambda 表达式,您需要创建自己的函数式接口或使用 Java 提供的预定义函数式接口。只有单个抽象方法的接口称为函数式接口(或单抽象方法接口),例如:Runnablecallable,ActionListener等。

使用函数式接口:

Java 8 之前:我们创建匿名内部类。
Java 8 之后:您可以使用 lambda 表达式而不是匿名内部类。

Java Lambda 表达式示例

不使用 Lambda 表达式:在 java 8 之前,我们使用匿名内部类来实现函数式接口的唯一抽象方法。

import java.awt.*;  
import java.awt.event.*;  
public class ButtonListenerOldWay {  
    public static void main(String[] args) {  
       Frame frame=new Frame("ActionListener Before Java8");  

       Button b=new Button("Click Here");  
       b.setBounds(50,100,80,50);  

       b.addActionListener(new ActionListener(){  
          public void actionPerformed(ActionEvent e){  
    	     System.out.println("Hello World!"); 
          }  
       });  
       frame.add(b);

       frame.setSize(200,200);  
       frame.setLayout(null);  
       frame.setVisible(true);   
    }  
}

使用 Lambda 表达式:我们可以像这样创建一个 lambda 表达式,而不是创建匿名内部类:

import java.awt.*;  
public class ButtonListenerNewWay {  
   public static void main(String[] args) {  
      Frame frame=new Frame("ActionListener java8");  

      Button b=new Button("Click Here");  
      b.setBounds(50,100,80,50); 

      b.addActionListener(e -> System.out.println("Hello World!")); 
      frame.add(b);

      frame.setSize(200,200);  
      frame.setLayout(null);  
      frame.setVisible(true);   
   }  
}

注意:

  1. 正如您所看到的,我们使用较少的代码与 lambda 表达式。
  2. 向后兼容性:您可以将 lambda 表达式与旧代码一起使用。 Lambdas 是向后兼容的,因此当您将项目迁移到 Java 8 时,可以在现有 API 中使用它们。

让我们看几个 Lambda 表达式的例子。

示例 1:没有参数的 Java Lambda 表达式

@FunctionalInterface
interface MyFunctionalInterface {

	//A method with no parameter
    public String sayHello();
}
public class Example {

   public static void main(String args[]) {
        // lambda expression
    	MyFunctionalInterface msg = () -> {
    		return "Hello";
    	};
        System.out.println(msg.sayHello());
    }
}

输出:

Hello

示例 2:具有单个参数的 Java Lambda 表达式

@FunctionalInterface
interface MyFunctionalInterface {

	//A method with single parameter
    public int incrementByFive(int a);
}
public class Example {

   public static void main(String args[]) {
        // lambda expression with single parameter num
    	MyFunctionalInterface f = (num) -> num+5;
        System.out.println(f.incrementByFive(22));
    }
}

输出:

27

示例 3:具有多个参数的 Java Lambda 表达式

interface StringConcat {

    public String sconcat(String a, String b);
}
public class Example {

   public static void main(String args[]) {
        // lambda expression with multiple arguments
    	StringConcat s = (str1, str2) -> str1 + str2;
        System.out.println("Result: "+s.sconcat("Hello ", "World"));
    }
}

输出:

Result: Hello World

示例 4:使用foreach循环迭代集合

import java.util.*;  
public class Example{  
    public static void main(String[] args) {       
       List<String> list=new ArrayList<String>();  
       list.add("Rick");         
       list.add("Negan");       
       list.add("Daryl");         
       list.add("Glenn");         
       list.add("Carl");                
       list.forEach(          
           // lambda expression        
           (names)->System.out.println(names)         
       );     
    }  
}

我们可以使用 lambda 表达式迭代 Maps 和其他集合类,参考本指南:使用 lambda 表达式迭代 Map 和 List

Java 中的变量

原文: https://beginnersbook.com/2017/08/variables-in-java/

变量是与可以更改的值相关联的名称。例如,当我写int i=10;时,变量名是 i ,它与值 10 相关联,int是表示该变量可以保存整数值的数据类型。我们将在下一个教程中介绍数据类型。在本教程中,我们将讨论变量。

如何在 Java 中声明变量

要声明变量,请遵循以下语法:

data_type variable_name = value;

这里的值是可选的,因为在 java 中,您可以先声明变量,然后再将值赋给它。

例如:这里num是一个变量,int是一个数据类型。我们将在下一个教程中讨论数据类型,所以不要过于担心它,只要理解int数据类型允许这个num变量保存整数值。您可以在此处阅读数据类型,但我建议您在继续阅读本指南之前先阅读本指南。

int num;

类似地,我们可以在声明它们的同时将值分配给变量,如下所示:

char ch = 'A';
int number = 100;

或者我们可以这样做:

char ch;
int number;
...
ch = 'A';
number  = 100;

java 中的变量命名约定

1)变量命名不能包含空格,例如:int num ber = 100;无效,因为变量名称中包含空格。

2)变量名可以以特殊字符开头,例如$_

3)根据 java 编码标准,变量名称应以小写字母开头,例如int number;对于具有多个单词的冗长变量名称,请执行以下操作:int smallNumber; int bigNumber;(以大写字母开头第二个字)。

4)变量名在 Java 中区分大小写。

Java 中的变量类型

Java 中有三种类型的变量

1)局部变量 2)静态(或类)变量 3)实例变量

静态(或类)变量

静态变量也称为类变量,因为它们与类相关联,并且对于所有类实例都是通用的。例如,如果我创建一个类的三个对象并访问此静态变量,那么对所有人来说都是常见的,使用其中一个对象对变量所做的更改将反映在您通过其他对象访问它时。

静态变量的示例

public class StaticVarExample {
   public static String myClassVar="class or static variable";

   public static void main(String args[]){
      StaticVarExample obj = new StaticVarExample();
      StaticVarExample obj2 = new StaticVarExample();
      StaticVarExample obj3 = new StaticVarExample();

      //All three will display "class or static variable"
      System.out.println(obj.myClassVar);
      System.out.println(obj2.myClassVar);
      System.out.println(obj3.myClassVar);

      //changing the value of static variable using obj2
      obj2.myClassVar = "Changed Text";

      //All three will display "Changed Text"
      System.out.println(obj.myClassVar);
      System.out.println(obj2.myClassVar);
      System.out.println(obj3.myClassVar);
   }
}

输出:

class or static variable
class or static variable
class or static variable
Changed Text
Changed Text
Changed Text

正如您所看到的,无论访问它的实例如何,所有三个语句都显示相同的输出。这就是为什么我们可以在不使用这样的对象的情况下访问静态变量:

System.out.println(myClassVar);

请注意,只能像这样访问静态变量。这不适用于实例和局部变量。

实例变量

类的每个实例(对象)都有自己的实例变量副本。与静态变量不同,实例变量有自己独立的实例变量副本。我们在下面的程序中使用对象obj2更改了实例变量值,当我们使用所有三个对象显示变量时,只有obj2值被更改,其他值保持不变。这表明它们有自己的实例变量副本。

实例变量的示例

public class InstanceVarExample {
   String myInstanceVar="instance variable";

   public static void main(String args[]){
	InstanceVarExample obj = new InstanceVarExample();
	InstanceVarExample obj2 = new InstanceVarExample();
	InstanceVarExample obj3 = new InstanceVarExample();

	System.out.println(obj.myInstanceVar);
	System.out.println(obj2.myInstanceVar);
	System.out.println(obj3.myInstanceVar);

	obj2.myInstanceVar = "Changed Text";

	System.out.println(obj.myInstanceVar);
	System.out.println(obj2.myInstanceVar);
	System.out.println(obj3.myInstanceVar);
   }
}

输出:

instance variable
instance variable
instance variable
instance variable
Changed Text
instance variable

局部变量

这些变量在类的方法内声明。它们的范围仅限于方法,这意味着您无法更改其值并在方法之外访问它们。

在这个例子中,我已经声明了与局部变量同名的实例变量,这是为了演示局部变量的范围。

局部变量的示例

public class VariableExample {
   // instance variable
   public String myVar="instance variable";

   public void myMethod(){
    	// local variable
    	String myVar = "Inside Method";
    	System.out.println(myVar);
   }
   public static void main(String args[]){
      // Creating object
      VariableExample obj = new VariableExample();

      /* We are calling the method, that changes the 
       * value of myVar. We are displaying myVar again after 
       * the method call, to demonstrate that the local 
       * variable scope is limited to the method itself.
       */
      System.out.println("Calling Method");
      obj.myMethod();
      System.out.println(obj.myVar);
   }
}

输出:

Calling Method
Inside Method
instance variable

如果我没有声明实例变量并且只在方法内部声明了局部变量,那么语句System.out.println(obj.myVar);会抛出编译错误。因为您无法更改和访问方法之外的局部变量。

在继续下一个主题之前,请查看这些相关的 java 示例

  1. Java 程序:查找字符的 ASCII 值
  2. Java 程序:乘以两个数字
  3. Java 程序:计算三角区域

Java 8 中的方法引用

原文: https://beginnersbook.com/2017/10/method-references-in-java-8/

在上一个教程中,我们在 Java 8 中学习了 lambda 表达式。这里我们将讨论 java 8 的另一个新特性,方法引用。方法引用是用于调用方法的 lambda 表达式的简写表示法。例如:
如果你的 lambda 表达式是这样的:

str -> System.out.println(str)

然后你可以用这样的方法引用替换它:

System.out::println

::运算符用于方法引用,以将类或对象与方法名称分开(我们将在示例的帮助下学习)。

四种方法引用

  1. 对象的实例方法的方法引用 - object::instanceMethod
  2. 类的静态方法的方法引用 - class::staticMethod
  3. 特定类的任意对象的实例方法的方法引用 - Class::instanceMethod
  4. 构造函数的方法引用 - Class::new

1.方法引用:对象的实例方法

@FunctionalInterface 
interface MyInterface{  
    void display();  
}  
public class Example {  
    public void myMethod(){  
	System.out.println("Instance Method");  
    }  
    public static void main(String[] args) {  
	Example obj = new Example();   
	// Method reference using the object of the class
	MyInterface ref = obj::myMethod;  
	// Calling the method of functional interface  
	ref.display();  
    }  
}

输出:

Instance Method

2.方法引用:类的静态方法

import java.util.function.BiFunction;  
class Multiplication{  
   public static int multiply(int a, int b){  
	return a*b;  
   }  
}  
public class Example {  
   public static void main(String[] args) {  
	BiFunction<Integer, Integer, Integer> product = Multiplication::multiply;  
	int pr = product.apply(11, 5);  
	System.out.println("Product of given number is: "+pr);  
   }  
}

输出:

Product of given number is: 55

3.方法引用:特定类型的任意对象的实例方法

import java.util.Arrays;
public class Example {  

   public static void main(String[] args) {  
	String[] stringArray = { "Steve", "Rick", "Aditya", "Negan", "Lucy", "Sansa", "Jon"};
	/* Method reference to an instance method of an arbitrary 
	 * object of a particular type
	 */
	Arrays.sort(stringArray, String::compareToIgnoreCase);
	for(String str: stringArray){
		System.out.println(str);
	}
   }  
}

输出:

Aditya
Jon
Lucy
Negan
Rick
Sansa
Steve

4.构造函数的方法引用

@FunctionalInterface 
interface MyInterface{  
    Hello display(String say);  
}  
class Hello{  
    public Hello(String say){  
        System.out.print(say);  
    }  
}  
public class Example {  
    public static void main(String[] args) { 
    	//Method reference to a constructor
        MyInterface ref = Hello::new;  
        ref.display("Hello World!");  
    }  
}

输出:

Hello World!

Java 函数式接口

原文: https://beginnersbook.com/2017/10/java-functional-interfaces/

只有单个抽象方法的接口称为函数式接口。您可以使用 Java 提供的预定义函数式接口,也可以创建自己的函数式接口并使用它。您可以在这里检查预定义的函数式接口:预定义的函数式接口它们都只有一个抽象方法。这就是原因,它们也被称为单抽象方法接口(SAM 接口)。

要在 Java 中使用 lambda 表达式,您需要创建自己的函数式接口或使用 Java 提供的预定义函数式接口。在创建自己的函数式接口时,用@FunctionalInterface注解标记它,这个注解是在 Java 8 中引入的。尽管它是可选的,你应该使用它,这样如果你用这个标记的接口就会出现编译错误注解不遵循函数式接口的规则。

定义函数式接口的规则是什么?

函数式接口应该有只有一个抽象方法。除了一个抽象方法,它们还可以包含任意数量的默认和静态方法。

示例 1:创建自己的函数式接口

@FunctionalInterface
interface MyFunctionalInterface {

    public int addMethod(int a, int b);
}
public class BeginnersBookClass {

   public static void main(String args[]) {
        // lambda expression
    	MyFunctionalInterface sum = (a, b) -> a + b;
        System.out.println("Result: "+sum.addMethod(12, 100));
    }
}

输出:

Result: 112

示例 2:使用预定义的函数式接口

import java.util.function.IntBinaryOperator;

public class BeginnersBookClass {

   public static void main(String args[]) {
        // lambda expression
        IntBinaryOperator sum = (a, b) -> a + b;
        System.out.println("Result: " + sum.applyAsInt(12, 100));

    }
}

输出:

Result: 112

函数式接口示例:使用匿名内部类 vs 使用 lambda 表达式

我们在 java8 之前就已经使用了函数式接口,它们是通过使用这些接口创建匿名内部类来使用的。你必须已经看过像RunnableActionListenerComparator等函数式接口。它们都有单一的抽象方法。让我们看一个ActionListener的例子,看看它如何与Anonymous内部类一起使用,以及如何使用 lambda 表达式实现它。

ActionListener示例:在 Java 8 之前:使用匿名内部类

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Example extends JFrame
{
    JButton button;
    public Example()
    {
	setTitle("Button Action Example without Lambda Expression");
	setSize(400,300);
	setVisible(true);
	setLayout(new FlowLayout());
	setDefaultCloseOperation(EXIT_ON_CLOSE);

	button = new JButton("Button");
	button.setBounds(100,100,90,40);
	button.addActionListener(new ActionListener(){  
	   public void actionPerformed(ActionEvent e){  
		System.out.println("You clicked the button."); 
	   }  

	});
	add(button);
    }
    public static void main(String args[])
    {
	new Example();
    }   
}

ActionListener示例:Lambda 表达式

import javax.swing.*;
import java.awt.*;
class Example extends JFrame
{
    JButton button;
    public Example()
    {
	setTitle("Button Action Example using Lambda Expression");
	setSize(400,300);
	setVisible(true);
	setLayout(new FlowLayout());
	setDefaultCloseOperation(EXIT_ON_CLOSE);

	button = new JButton("Button");
	button.setBounds(100,100,90,40);
	//Lambda expression
	button.addActionListener(e-> 
	   System.out.println("You clicked the button.")); 

	add(button);
    }
    public static void main(String args[])
    {
	new Example();
    }   
}

Java 8 流教程

原文: https://beginnersbook.com/2017/10/java-8-stream-tutorial/

在上一个教程中,我们学习了 java 8 中的接口更改。在本指南中,我们将讨论流 API,这是 java 8 的另一个新功能。该 API 的所有类和接口都在java.util.stream包中。通过使用流,我们可以对从集合,数组,输入/输出操作返回的数据执行各种聚合操作。在我们看到如何在 Java 中使用流 API 之前,让我们看一个例子来理解流的使用。

Java 流示例

要了解流的工作原理,让我们在不使用流的情况下进行示例,然后我们将看到与流相同的示例。

在不使用流的情况下查找某些字符串

import java.util.ArrayList;
import java.util.List;
public class Example{ 
   public static void main(String[] args) {    
	List<String> names = new ArrayList<String>();
	names.add("Ajeet");
	names.add("Negan");
	names.add("Aditya");
	names.add("Steve");
	int count = 0;
	for (String str : names) {
	   if (str.length() < 6) 
		count++; 
	}
        System.out.println("There are "+count+" strings with length less than 6");
   }  
}

输出:

There are 3 strings with length less than 6

使用流的相同示例

import java.util.ArrayList;
import java.util.List;
public class Example{ 
   public static void main(String[] args) {    
	List<String> names = new ArrayList<String>();
	names.add("Ajeet");
	names.add("Negan");
	names.add("Aditya");
	names.add("Steve");

	//Using Stream and Lambda expression
	long count = names.stream().filter(str->str.length()<6).count();
	System.out.println("There are "+count+" strings with length less than 6");

   }  
}

输出:

There are 3 strings with length less than 6

这些代码有什么区别?

两个示例的输出都是相同的,但是如果考虑代码的性能,这些示例之间会有很大的不同。
在第一个例子中,我们迭代整个列表以找到长度小于 6 的字符串。此代码中没有并行性。
在第二个例子中,stream()方法返回所有名称的流,filter()方法返回另一个长度小于 6 的名称流,count()方法减少了这个流到结果。所有这些操作都是并行发生的,这意味着我们能够在流的帮助下并行化代码。 使用流并行执行操作比不使用流的顺序执行更快。

如何在 Java 中使用流

正如我们在上面的例子中看到的那样,流的工作可以分三个阶段来解释:

  1. 创建一个流

  2. 在初始流上执行中间操作,将其转换为另一个流,依此类推进一步的中间操作。在上面的例子中,filter()操作是中间操作,可以有多个中间操作。

  3. 在最终流上执行端子操作以获得结果。在上面的例子中,count()操作是终端操作。

Java 流函数

  1. 不存储元素。它只是执行聚合操作(例如我们在上面的例子中看到的filter()count())来获得所需的数据流。

  2. 我们对集合,数组或任何其他数据源执行的聚合操作不会更改源的数据,它们只返回一个新流。例如,我们上面看到的代码是使用流操作过滤长度小于 6 的字符串,但它没有更改列表的元素。

  3. 所有流操作本质上都是懒惰,这意味着它们在需要之前不会被执行。例如,如果我们想要使用流仅显示列表的前 2 个元素,则在显示列表的第二个元素之后,流操作将在第二次迭代结束时停止。

让我们看几个 Java 流的例子:

Java 流示例 1:迭代并显示选定的整数

import java.util.stream.*;  
public class Example {  
    public static void main(String[] args){  
        Stream.iterate(1, count->count+1)  
        .filter(number->number%3==0)  
        .limit(6)  
        .forEach(System.out::println);  
    }  
}

输出:

3
6
9
12
15
18

Java 流示例 2:连接两个流

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Example {
   public static void main(String[] args) {
	//list 1
	List<String> alphabets = Arrays.asList("A","B","C");
	//list 2
	List<String> names = Arrays.asList("Sansa","Jon","Arya");

	//creating two streams from the two lists and concatenating them into one
	Stream<String> opstream = Stream.concat(alphabets.stream(), names.stream());

	//displaying the elements of the concatenated stream
	opstream.forEach(str->System.out.print(str+" "));
   }
}

输出:

A B C Sansa Jon Arya

相关文章:

  1. Java 流allMatch()示例
  2. Java 流noneMatch()示例
  3. Java 流anyMatch()示例

Java 8 流过滤器

原文: https://beginnersbook.com/2017/10/java-8-stream-filter/

在上一个教程中,我们了解了 Java 流 。在完成本教程之前,我建议您阅读该指南。在本指南中,我们将讨论 Java 流过滤器。 filter()是一个中间操作,它从流中读取数据并在根据给定条件转换数据后返回新流。让我们先看一个简单的例子,然后我们将看到流过滤器的其他方法的示例。

Java 流的filter()的一个简单示例

在这个例子中,我们使用stream()方法从名称列表创建一个流,然后我们使用流的filter()创建另一个长名称流。如上所述,流过滤器将一个流的数据转换为另一个流。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Example {
   public static void main(String[] args) {

	List<String> names = Arrays.asList("Melisandre","Sansa","Jon","Daenerys","Joffery");

	//Creating the stream of all names
	Stream<String> allNames = names.stream();

	//Creating another stream by filtering long names using filter()
	Stream<String> longNames = allNames.filter(str -> str.length() > 6);

	//displaying the long names
	longNames.forEach(str->System.out.print(str+" "));
  }
}

输出:

Melisandre Daenerys Joffery

让我们再看几个 Java 流过滤器的例子。

示例 1:流的filter()collect()

我们可以创建一个流并在一行中应用过滤器,如下例所示。这里的collect()方法收集最终流并将其转换为列表

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Example {

    public static void main(String[] args) {

        List<String> names = Arrays.asList("Melisandre","Sansa","Jon","Daenerys","Joffery");

        List<String> longnames = names.stream()    // converting the list to stream
                .filter(str -> str.length() > 6)   // filter the stream to create a new stream
                .collect(Collectors.toList());  // collect the final stream and convert it to a List

        longnames.forEach(System.out::println);           

    }

}

输出:

Melisandre
Daenerys
Joffery

示例 2:具有多个条件的流filter()

在上面的例子中,我们已经看到filter()方法中只有一个条件。我们可以在使用 java 中的逻辑运算符连接的filter()方法中有多个条件。在下面的示例中,我们在使用和(&&)逻辑运算符连接的filter方法中有两个条件。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Example {

    public static void main(String[] args) {

        List<String> names = Arrays.asList("Melisandre","Sansa","Jon","Daenerys","Joffery");

        List<String> longnames = names.stream()  
                .filter(str -> str.length() > 6 && str.length() < 8) //Multiple conditions
                .collect(Collectors.toList());  

        longnames.forEach(System.out::println);           

    }

}

输出:

Joffery

示例 3:Java 中的流的filter()map()方法

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Example {

    public static void main(String[] args) {

    	List<Integer> num = Arrays.asList(1,2,3,4,5,6);
        List<Integer> squares = num.stream()
        		.map(n -> n * n)
        		.collect(Collectors.toList());
        System.out.println(squares);        

    }

}

输出:

[1, 4, 9, 16, 25, 36]

更多 Java 流过滤器的教程:

  1. Java 8 - 按键和值过滤映射
  2. Java 8 - 从流中过滤空值

Java 8 接口更改 - 默认方法和静态方法

原文: https://beginnersbook.com/2017/10/java-8-interface-changes-default-method-and-static-method/

在 java 8 之前,java 中的接口只能有抽象方法。接口的所有方法都是公共的和默认是抽象的。 Java 8 允许接口具有默认和静态方法。我们在接口中使用默认方法的原因是允许开发人员向接口添加新方法,而不会影响实现这些接口的类。

为什么默认方法?

例如,如果ABCD等几个类实现了接口XYZInterface,那么如果我们向XYZInterface添加新方法,我们必须更改所有代码实现此接口的类(ABCD)。在这个例子中,我们只有四个实现我们想要改变的接口的类,但想象如果有数百个类实现一个接口,那么几乎不可能改变所有这些类中的代码。这就是为什么在 java 8 中,我们有一个新概念“默认方法”。这些方法可以添加到任何现有接口,我们不需要在实现类中强制实现这些方法,因此我们可以将这些默认方法添加到现有接口而不会破坏代码。

我们可以说在 java 8 中引入了默认方法的概念,以这种方式在现有接口中添加新方法,使它们向后兼容。向后兼容性是在不破坏旧代码的情况下添加新功能。

接口中的静态方法类似于默认方法,除了我们不能在实现这些接口的类中覆盖这些方法。

Java 8 示例:接口中的默认方法

MyInterface中的方法newMethod()是一个默认方法,这意味着我们不需要在实现类Example中实现这个方法。这样我们就可以将默认方法添加到现有接口,而无需担心实现这些接口的类。

interface MyInterface{  
    /* This is a default method so we need not
     * to implement this method in the implementation 
     * classes  
     */
    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  
    /* Already existing public and abstract method
     * We must need to implement this method in 
     * implementation classes.
     */
    void existingMethod(String str);  
}  
public class Example implements MyInterface{ 
	// implementing abstract method
    public void existingMethod(String str){           
        System.out.println("String is: "+str);  
    }  
    public static void main(String[] args) {  
    	Example obj = new Example();

    	//calling the default method of interface
        obj.newMethod();     
        //calling the abstract method of interface
        obj.existingMethod("Java 8 is easy to learn"); 

    }  
}

输出:

Newly added default method
String is: Java 8 is easy to learn

Java 8 示例:接口中的静态方法

如上所述,接口中的静态方法与默认方法类似,因此我们不需要在实现类中实现它们。我们可以安全地将它们添加到现有接口,而无需更改实现类中的代码。由于这些方法是静态的,我们不能在实现类中覆盖它们。

interface MyInterface{  
    /* This is a default method so we need not
     * to implement this method in the implementation 
     * classes  
     */
    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  

    /* This is a static method. Static method in interface is
     * similar to default method except that we cannot override 
     * them in the implementation classes.
     * Similar to default methods, we need to implement these methods
     * in implementation classes so we can safely add them to the 
     * existing interfaces.
     */
    static void anotherNewMethod(){
    	System.out.println("Newly added static method");
    }
    /* Already existing public and abstract method
     * We must need to implement this method in 
     * implementation classes.
     */
    void existingMethod(String str);  
}  
public class Example implements MyInterface{ 
	// implementing abstract method
    public void existingMethod(String str){           
        System.out.println("String is: "+str);  
    }  
    public static void main(String[] args) {  
    	Example obj = new Example();

    	//calling the default method of interface
        obj.newMethod();     
        //calling the static method of interface
        MyInterface.anotherNewMethod();
        //calling the abstract method of interface
        obj.existingMethod("Java 8 is easy to learn"); 

    }  
}

输出:

Newly added default method
Newly added static method
String is: Java 8 is easy to learn

Java 8 - 抽象类与接口

通过在接口中引入默认方法,似乎抽象类与 java 8 中的接口相同。然而,这并非完全正确,即使我们现在可以使用具体方法(带有 body 的方法)接口就像抽象类一样,这并不意味着它们是相同的。它们之间仍然存在很少的差异,其中之一是抽象类可以有构造函数,而在接口中我们不能有构造函数。

接口的目的是提供完全抽象,而抽象类的目的是提供部分抽象。这仍然适用。接口就像是您类的蓝图,通过引入默认方法,您可以简单地说我们可以在接口中添加其他功能而不会影响最终用户类。

默认方法和多重继承

当我们有两个具有相同签名的默认方法的接口时,可能会发生多重继承问题。让我们举个例子。

interface MyInterface{  

    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  
    void existingMethod(String str);  
}  
interface MyInterface2{  

    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  
    void disp(String str);  
} 
public class Example implements MyInterface, MyInterface2{ 
	// implementing abstract methods
    public void existingMethod(String str){           
        System.out.println("String is: "+str);  
    }  
    public void disp(String str){
    	System.out.println("String is: "+str); 
    }

    public static void main(String[] args) {  
    	Example obj = new Example();

    	//calling the default method of interface
        obj.newMethod();     

    }  
}

输出:

Error: Duplicate default methods named newMethod with the parameters () and () are inherited from the types MyInterface2 and MyInterface

这是因为我们在接口和编译器中都有相同的方法,不知道要调用哪个方法。

如何解决这个问题?为了解决这个问题,我们可以在实现类中实现这个方法,如下所示:

interface MyInterface{  

    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  
    void existingMethod(String str);  
}  
interface MyInterface2{  

    default void newMethod(){  
        System.out.println("Newly added default method");  
    }  
    void disp(String str);  
} 
public class Example implements MyInterface, MyInterface2{ 
	// implementing abstract methods
    public void existingMethod(String str){           
        System.out.println("String is: "+str);  
    }  
    public void disp(String str){
    	System.out.println("String is: "+str); 
    }
    //Implementation of duplicate default method
    public void newMethod(){  
        System.out.println("Implementation of default method");  
    }  
    public static void main(String[] args) {  
    	Example obj = new Example();

    	//calling the default method of interface
        obj.newMethod();     

    }  
}

输出:

Implementation of default method

Java 8 forEach方法

原文: https://beginnersbook.com/2017/10/java-8-foreach/

在 Java 8 中,我们有一个新引入的forEach方法来迭代 Java 中的集合。在本指南中,我们将学习如何使用forEach()forEachOrdered()方法来循环特定的集合和流。

Java 8 - forEach迭代Map

import java.util.Map;
import java.util.HashMap;
public class Example {
   public static void main(String[] args) {
      Map<Integer, String> hmap = new HashMap<Integer, String>();
      hmap.put(1, "Monkey");
      hmap.put(2, "Dog"); 
      hmap.put(3, "Cat");  
      hmap.put(4, "Lion");   
      hmap.put(5, "Tiger");   
      hmap.put(6, "Bear");
      /* forEach to iterate and display each key and value pair
       * of HashMap.    
       */  
      hmap.forEach((key,value)->System.out.println(key+" - "+value));
      /* forEach to iterate a Map and display the value of a particular  
       * key     
       */ 
      hmap.forEach((key,value)->{ 
         if(key == 4){ 
            System.out.println("Value associated with key 4 is: "+value); 
         }  
      });    
      /* forEach to iterate a Map and display the key associated with a
       * particular value     
       */
      hmap.forEach((key,value)->{
         if("Cat".equals(value)){ 
            System.out.println("Key associated with Value Cat is: "+key);
         }
      }); 
   }
}

输出:

java 8 foreach example

Java 8 - forEach迭代List

在这个例子中,我们使用forEach()方法迭代ArrayList。在forEach中,我们使用 lambda 表达式来打印列表的每个元素。

import java.util.List;
import java.util.ArrayList;
public class Example {
   public static void main(String[] args) {
      List<String> fruits = new ArrayList<String>();
      fruits.add("Apple");
      fruits.add("Orange");
      fruits.add("Banana");
      fruits.add("Pear"); 
      fruits.add("Mango");
      //lambda expression in forEach Method 
      fruits.forEach(str->System.out.println(str));
   }
}

输出:

Apple
Orange
Banana
Pear
Mango

我们也可以在forEach()方法中使用方法引用,如下所示:

fruits.forEach(System.out::println);

Java 8 - 迭代流的forEach方法

在这个例子中,我们使用forEach()方法在 Java 中迭代

import java.util.List;
import java.util.ArrayList;
public class Example {
   public static void main(String[] args) {
      List<String> names = new ArrayList<String>();
      names.add("Maggie");
      names.add("Michonne");
      names.add("Rick");
      names.add("Merle");
      names.add("Governor");
      names.stream() //creating stream 
     .filter(f->f.startsWith("M")) //filtering names that starts with M 
     .forEach(System.out::println); //displaying the stream using forEach
   }
}

输出:

Maggie
Michonne
Merle

Java - Stream forEachOrdered()方法示例

对于顺序流,元素的顺序与源中的顺序相同,因此无论使用forEach还是forEachOrdered,输出都是相同的。但是,在处理并行流时,您总是希望在订单对您很重要时使用forEachOrdered()方法,因为此方法可确保元素的顺序与源相同。让我们举个例子来理解forEach()forEachOrdered()之间的区别。

import java.util.List;
import java.util.ArrayList;
public class Example {
   public static void main(String[] args) {
      List<String> names = new ArrayList<String>();
      names.add("Maggie"); 
      names.add("Michonne");
      names.add("Rick");
      names.add("Merle");
      names.add("Governor"); 
      //forEach - the output would be in any order
      System.out.println("Print using forEach");
      names.stream() 
     .filter(f->f.startsWith("M"))
     .parallel() 
     .forEach(n->System.out.println(n)); 

     /* forEachOrdered - the output would always be in this order: 
      * Maggie, Michonne, Merle 
      */ 
     System.out.println("Print using forEachOrdered"); 
     names.stream()  
     .filter(f->f.startsWith("M"))  
     .parallel() 
     .forEachOrdered(n->System.out.println(n));
   }
}

输出:

Print using forEach
Merle
Maggie
Michonne
Print using forEachOrdered
Maggie
Michonne
Merle

参考文献:

Java 8 - Stream Collectors

原文: https://beginnersbook.com/2017/10/java-8-stream-collectors-class-with-examples/

收集器是最终类,它扩展了Object类。在本教程中,我们将看到使用 lambda 表达式Java 流 和 Java 8 的其他新功能的 Java 流收集器类的示例。

java.lang.Object
   |
   |___java.util.stream.Collectors

Java - Stream Collectors groupingBy和计数示例

在此示例中,我们使用Collectors类的groupingBy()方法对列表的元素进行分组,并打印列表中每个元素的出现次数。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Example {

   public static void main(String[] args) {

      List<String> names =
          Arrays.asList("Jon", "Ajeet", "Steve",
             "Ajeet", "Jon", "Ajeet");

      Map<String, Long> map =
      names.stream().collect(
          Collectors.groupingBy(
             Function.identity(), Collectors.counting()
          )
      );

      System.out.println(map);

   }
}

输出:

{Steve=1, Jon=2, Ajeet=3}

Java - Stream Collector将数据提取为List的示例

import java.util.stream.Collectors;  
import java.util.List;  
import java.util.ArrayList;  
class Student{  
   int id;     
   String name;    
   int age;         
   public Student(int id, String name, int age) {  
        this.id = id;    
        this.name = name;         
        this.age = age;     
   } 
}  
public class Example {  
   public static void main(String[] args) {    
      List<Student> studentlist = new ArrayList<Student>();   
      //Adding Students      
      studentlist.add(new Student(11,"Jon",22));      
      studentlist.add(new Student(22,"Steve",18));        
      studentlist.add(new Student(33,"Lucy",22));        
      studentlist.add(new Student(44,"Sansa",23));         
      studentlist.add(new Student(55,"Maggie",18));                  
      //Fetching student names as List       
      List<String> names = studentlist.stream() 
                                   .map(n->n.name) 
                                   .collect(Collectors.toList());
      System.out.println(names);         
   }  
}

输出:

[Jon, Steve, Lucy, Sansa, Maggie]

Java 收集器示例 - 将数据收集为集合

在这个例子中,我们将学生列表转换为,然后我们应用 Java Stream过滤器从流中获取所选记录,之后我们将使用Collectors.toSet()方法,将该流转换为集合。

import java.util.stream.Collectors;  
import java.util.List;  
import java.util.Set; 
import java.util.ArrayList;  
class Student{    
   int id;     
   String name;  
   int age;           
   public Student(int id, String name, int age) {   
       this.id = id;         
       this.name = name;       
       this.age = age;      
   } 
}  
public class Example {     
   public static void main(String[] args) {       
      List<Student> studentlist = new ArrayList<Student>();       
      //Adding Students        
      studentlist.add(new Student(11,"Jon",22));         
      studentlist.add(new Student(22,"Steve",18));         
      studentlist.add(new Student(33,"Lucy",22));         
      studentlist.add(new Student(44,"Sansa",23));         
      studentlist.add(new Student(55,"Maggie",18));                  
      //Fetching student data as a Set       
      Set<Student> students = studentlist.stream()
                           .filter(n-> n.id>22)
                           .collect(Collectors.toSet());
      //Iterating Set       
      for(Student stu : students) { 
         System.out.println(stu.id+" "+stu.name+" "+stu.age); 
      }           
   } 
}

输出:

44 Sansa 23
33 Lucy 22
55 Maggie 18

Java 收集器示例 - 使用averageInt()方法获取学生的平均年龄

import java.util.stream.Collectors;  
import java.util.List;  
import java.util.ArrayList; 
class Student{  
   int id;   
   String name;   
   int age;
   public Student(int id, String name, int age) {  
      this.id = id;   
      this.name = name; 
      this.age = age;  
   } 
}  
public class Example {  
   public static void main(String[] args) {  
      List<Student> studentlist = new ArrayList<Student>();  
      //Adding Students  
      studentlist.add(new Student(11,"Jon",22));   
      studentlist.add(new Student(22,"Steve",18));   
      studentlist.add(new Student(33,"Lucy",22));   
      studentlist.add(new Student(44,"Sansa",23));   
      studentlist.add(new Student(55,"Maggie",18));
      //Getting the average Age 
      Double avgAge = studentlist.stream()   
          .collect(Collectors.averagingInt(s->s.age));  
      System.out.println("Average Age of Students is: "+avgAge);
   }  
}

输出:

Average Age of Students is: 20.6

我已经展示了 Java Collectors类的极少数方法的例子。要获得完整的方法列表,请参阅 javadoc:Java 8 Stream Collectors - JavaDoc

Java 8 StringJoiner

原文: https://beginnersbook.com/2017/10/java-8-stringjoiner/

在 java 8 中,java.util包中引入了一个新类StringJoiner。使用这个类,我们可以使用指定的分隔符连接多个字符串,我们还可以在连接多个字符串时为最终字符串提供前缀和后缀。在本教程中,我们将看到几个StringJoiner类的示例,在本指南的最后,我们将看到StringJoiner类的方法。

Java StringJoiner示例 1:通过指定分隔符来连接字符串

在这个例子中,我们使用StringJoiner连接多个字符串。在创建StringJoiner的实例时,我们将分隔符指定为连字符(-)。

import java.util.StringJoiner;  
public class Example {  
    public static void main(String[] args) {  
    	// Passing Hyphen(-) as delimiter
        StringJoiner mystring = new StringJoiner("-");    

        // Joining multiple strings by using add() method  
        mystring.add("Logan");  
        mystring.add("Magneto");  
        mystring.add("Rogue");  
        mystring.add("Storm");  

        // Displaying the output String
        System.out.println(mystring);  
    }  
}

输出:

Logan-Magneto-Rogue-Storm

Java StringJoiner示例 2:为输出字符串添加前缀和后缀

import java.util.StringJoiner;  
public class Example {  
    public static void main(String[] args) {  
    	/* Passing comma(,) as delimiter and opening bracket
    	 * "(" as prefix and closing bracket ")" as suffix
    	 */
        StringJoiner mystring = new StringJoiner(",", "(", ")");    

        // Joining multiple strings by using add() method  
        mystring.add("Negan");  
        mystring.add("Rick");  
        mystring.add("Maggie");  
        mystring.add("Daryl");  

        // Displaying the output String
        System.out.println(mystring);  
    }  
}

输出:

(Negan,Rick,Maggie,Daryl)

StringJoiner示例 3:合并两个StringJoiner对象

import java.util.StringJoiner;  
public class Example {  
   public static void main(String[] args) {  
	/* Passing comma(,) as delimiter and opening bracket
	 * "(" as prefix and closing bracket ")" as suffix
	 */
	StringJoiner mystring = new StringJoiner(",", "(", ")");    

	mystring.add("Negan");  
	mystring.add("Rick");  
	mystring.add("Maggie");  
	mystring.add("Daryl");  

	System.out.println("First String: "+mystring);

	/* Passing hyphen(-) as delimiter and string "pre"
	 * as prefix and string "suff" as suffix
	 */
	StringJoiner myanotherstring = new StringJoiner("-", "pre", "suff");    

	myanotherstring.add("Sansa");  
	myanotherstring.add("Imp");  
	myanotherstring.add("Jon");  
	myanotherstring.add("Ned"); 

	System.out.println("Second String: "+myanotherstring);

	/* Merging both the strings  
	 * The important point to note here is that the output string will be 
	 * having the delimiter prefix and suffix of the first string (the string
	 * which is calling the merge method of StringJoiner)
	 */
	StringJoiner mergedString = mystring.merge(myanotherstring);   
	System.out.println(mergedString);  
   }  
}

输出:

First String: (Negan,Rick,Maggie,Daryl)
Second String: preSansa-Imp-Jon-Nedsuff
(Negan,Rick,Maggie,Daryl,Sansa-Imp-Jon-Ned)

在上面的例子中,我们已经看到了StringJoiner类的add()merge()方法。让我们看看这个类的其他方法。

StringJoiner示例:setEmptyValue()length()toString()方法

import java.util.StringJoiner;  
public class Example {  
    public static void main(String[] args) {  
    	//Comma(,) as delimiter
        StringJoiner mystring = new StringJoiner(",");   

        /* Using setEmptyValue() method, we can set the default value
         * of a StringJoiner instance, so if the StringJoiner is empty
         * and we print the value of it, this default value will be
         * displayed
         */
        mystring.setEmptyValue("This is a default String");  

        /* We have not added any string to StringJoiner yet so
         * this should display the default value of StringJoiner
         */
        System.out.println("Default String: "+mystring);  

        // Adding strings to StringJoiner  
        mystring.add("Apple");  
        mystring.add("Banana"); 
        mystring.add("Orange");
        mystring.add("Kiwi");
        mystring.add("Grapes");
        System.out.println(mystring);  

        /* The length() method of StringJoiner class returns the 
         * length of the string (the number of characters in the 
         * StringJoiner instance)
         */
        int length = mystring.length();  
        System.out.println("Length of the StringJoiner: "+length);  

        /* The toString() method is used for converting a StringJoiner
         *  instance to a String. 
         */
        String s = mystring.toString();  
        System.out.println(s);   
    }  
}

输出:

Default String: This is a default String
Apple,Banana,Orange,Kiwi,Grapes
Length of the StringJoiner: 31
Apple,Banana,Orange,Kiwi,Grapes

参考文献:

Java 8 - StringJoiner JavaDoc

Java 8 Optional

原文: https://beginnersbook.com/2017/10/java-8-optional-class/

在 Java 8 中,我们在java.util包中新引入了Optional类。引入此类是为了避免在我们的代码中不执行空检查时经常遇到的NullPointerException。使用这个类,我们可以很容易地检查变量是否具有null值,通过这样做,我们可以避免NullPointerException。在本指南中,我们将了解如何使用Optional类以及此类的各种方法的用法。

在我们看到Optional类的示例之前,让我们看看当我们不使用Optional类并且不执行null检查时会发生什么。

Java 示例:不使用Optional

在这个例子中,我们没有将值赋给String str,我们试图从中获取子串。由于str中没有值,程序抛出NullPointerException

public class Example {  
    public static void main(String[] args) {  
    	String[] str = new String[10];   
        //Getting the substring
        String str2 = str[9].substring(2, 5);
        //Displaying substring
        System.out.print(str2);  
    }  
}

输出:

Exception in thread "main" java.lang.NullPointerException
at Example.main(Example.java:5)

解决方案:使用Optional

Optional类的Optional.ofNullable()方法,如果给定对象有值,则返回非空Optional,否则返回空Optional
我们可以使用isPresent()方法检查返回的Optional值是空还是非空。

//Importing Optional class
import java.util.Optional; 
public class Example { 
   public static void main(String[] args) {    
      String[] str = new String[10];     
      Optional<String> isNull = Optional.ofNullable(str[9]);        
      if(isNull.isPresent()){     
         //Getting the substring           
         String str2 = str[9].substring(2, 5);          
         //Displaying substring           
         System.out.print("Substring is: "+ str2);       
      }     
      else{      
         System.out.println("Cannot get the substring from an empty string");     
      }                
      str[9] = "AgraIsCool";       
      Optional<String> isNull2 = Optional.ofNullable(str[9]);       
      if(isNull2.isPresent()){        
         //Getting the substring            
         String str2 = str[9].substring(2, 5);            
         //Displaying substring           
         System.out.print("Substring is: "+ str2);          
      }         
      else{         
         System.out.println("Cannot get the substring from an empty string");         
      }    
   }  
}

输出:

Cannot get the substring from an empty string
Substring is: raI

示例:OptionalisPresent()ifPresent()方法

在上面的例子中,我们已经看到通过使用isPresent()方法,我们可以检查特定的Optional对象(或实例)是空还是非空。
Optional类中还有另一种方法,只有在给定的Optional对象为非空时才执行,方法为ifPresent()。让我们看一个例子来理解差异。

//Importing Optional class
import java.util.Optional;
   public class Example {  
      public static void main(String[] args) {
         //Creating Optional object from a String
         Optional<String> GOT = Optional.of("Game of Thrones");        
         //Optional.empty() creates an empty Optional object        
         Optional<String> nothing = Optional.empty();
         /* isPresent() method: Checks whether the given Optional         
          * Object is empty or not.         
          */        
         if (GOT.isPresent()) {          
            System.out.println("Watching Game of Thrones");       
         } 
         else {            
            System.out.println("I am getting Bored");      
         }
         /* ifPresent() method: It executes only if the given Optional         
          * object is non-empty.         
          */        
         //This will print as the GOT is non-empty        
         GOT.ifPresent(s -> System.out.println("Watching GOT is fun!"));                
         //This will not print as the nothing is empty        
         nothing.ifPresent(s -> System.out.println("I prefer getting bored"));
   }
}

输出:

Watching Game of Thrones
Watching GOT is fun!

Java 8 - OptionalorElse()orElseGet()方法

如果该对象为空,则这两个方法orElse()orElseGet()返回Optional对象的值,如果该对象为空,则返回作为参数传递给此方法的默认值。

//Importing Optional class
import java.util.Optional;
   public class Example {  
      public static void main(String[] args) {
         //Creating Optional object from a String
         Optional<String> GOT = Optional.of("Game of Thrones");        
         //Optional.empty() creates an empty Optional object        
         Optional<String> nothing = Optional.empty();

         //orElse() method
         System.out.println(GOT.orElse("Default Value")); 
         System.out.println(nothing.orElse("Default Value")); 

         //orElseGet() method
         System.out.println(GOT.orElseGet(() -> "Default Value")); 
         System.out.println(nothing.orElseGet(() -> "Default Value")); 

    }
}

输出:

Game of Thrones
Default Value
Game of Thrones
Default Value

Java 8 - Optional.mapOptional.flatMap

在这个例子中,我们将看到Optional如何与mapflatMap一起使用。

//Importing Optional class
import java.util.Optional; 
public class Example {   
   public static void main(String[] args) {
      //Creating Optional object from a String       
      Optional<String> GOT = Optional.of("Game of Thrones");       
      //Optional.empty() creates an empty Optional object       
      Optional<String> nothing = Optional.empty();
      System.out.println(GOT.map(String::toLowerCase));        
      System.out.println(nothing.map(String::toLowerCase));
      Optional<Optional<String>> anotherOptional = Optional.of(Optional.of("BreakingBad"));        
      System.out.println("Value of Optional object"+anotherOptional);        
      System.out.println("Optional.map: "             
          +anotherOptional.map(gender -> gender.map(String::toUpperCase)));        
      //Optional<Optional<String>>    -> flatMap -> Optional<String>        
      System.out.println("Optional.flatMap: "            
          +anotherOptional.flatMap(gender -> gender.map(String::toUpperCase)));
   }
}

输出:

Optional[game of thrones]
Optional.empty
Value of Optional objectOptional[Optional[BreakingBad]]
Optional.map: Optional[Optional[BREAKINGBAD]]
Optional.flatMap: Optional[BREAKINGBAD]

示例:Optional和过滤器

在这个例子中,我们将看到Optional如何与过滤器一起使用。要阅读有关过滤器的信息,请参阅本指南: Java 过滤器
有关过滤器的更多教程:

  1. Java - 过滤映射
  2. 从 Java 的流中过滤空值
//Importing Optional class
import java.util.Optional; 
public class Example {  
   public static void main(String[] args) {
      //Creating Optional object from a String       
      Optional<String> GOT = Optional.of("Game of Thrones");              
      /* Filter returns an empty Optional instance if the output doesn't         
       * contain any value, else it returns the Optional object of the          
       * given value.         
       */        
      System.out.println(GOT.filter(s -> s.equals("GAME OF THRONES")));         
      System.out.println(GOT.filter(s -> s.equalsIgnoreCase("GAME OF THRONES")));
   }
}

输出:

Optional.empty
Optional[Game of Thrones]

参考文献:

Java 8 - Optional类 JavaDoc

Java 8 - 数组并行排序

原文: https://beginnersbook.com/2017/10/java-8-arrays-parallel-sort-with-example/

Java 8 在java.util包的Arrays类中引入了一个新方法parallelSort()。引入此方法以支持数组元素的并行排序。
并行排序算法:

  1. 将给定的数组划分为子数组,将子数组进一步划分为子数组,直到子数组达到最小粒度为止。
  2. 子数组由多个线程单独排序。并行排序使用 Fork / Join Framework 并行地对子数组进行排序。
  3. 已合并的已排序子数组。

并行排序优于简单排序的优点:

parallelSort()方法使用多线程的概念,与正常排序相比,有很多元素时它更快。

示例 1:对原始数据类型进行并行排序

import java.util.Arrays; 
public class Example {  
   public static void main(String[] args) {
	int numbers[] = {22, 89, 1, 32, 19, 5};
	//Parallel Sort method for sorting int array
	Arrays.parallelSort(numbers);
	//converting the array to stream and displaying using forEach
	Arrays.stream(numbers).forEach(n->System.out.print(n+" "));
    }
}

输出:

1 5 19 22 32 89

参考文献:

Java 8 - 并行排序 JavaDoc

示例 2:通过指定开始和结束索引进行并行排序

我们还可以指定排序的开始和结束,在这种情况下,从开始索引开始并在结束索引结束的子数组被排序,数组的其余部分被忽略并且不被排序。

import java.util.Arrays; 
public class Example {  
   public static void main(String[] args) {
	int numbers[] = {22, 89, 1, 32, 19, 5};
	/* Specifying the start and end index. The start index is
	 * 1 here and the end index is 5\. which means the the elements
	 * starting from index 1 till index 5 would be sorted.
	 */
	Arrays.parallelSort(numbers, 1, 5);
	//converting the array to stream and displaying using forEach
	Arrays.stream(numbers).forEach(n->System.out.print(n+" "));
   }
}

输出:

22 1 19 32 89 5

Java 中的数据类型

原文: https://beginnersbook.com/2017/08/data-types-in-java/

数据类型定义变量可以采用的值,例如,如果变量具有int数据类型,则它只能采用整数值。在 java 中,我们有两类数据类型:1)原始数据类型 2)非原始数据类型 - 数组和字符串是非原始数据类型,我们将在后面的教程中讨论它们。这里我们将讨论 Java 中的原始数据类型和字面值。

Java 是一种静态类型语言。如果在编译时已知变量的数据类型,则静态类型化语言。这意味着您必须先指定变量的类型(声明变量),然后才能使用它。

在上一篇关于 Java 变量的教程中,我们学会了如何声明一个变量,让我们回想一下:

int num;

因此,为了在程序中使用变量num,我们必须首先声明它,如上所示。在程序开头声明所有变量(您将要使用的)是一种很好的编程习惯。

1)原始数据类型

在 Java 中,我们有八种原始数据类型:booleancharbyteshortintlongfloatdouble。 Java 开发人员包含这些数据类型以维护 java 的可移植性,因为这些原始数据类型的大小不会从一个操作系统更改为另一个操作系统。

byteshortintlong数据类型用于存储整数。

floatdouble 用于分数。

char 用于存储字符(字母)。

boolean 数据类型用于包含truefalse的变量。

字节:

这可以保持-128 到 127 之间的整数。主要用于节省内存,当您确定数字将在字节数据类型指定的限制内时。

此数据类型的默认大小:1 个字节。

默认值:0

示例:

class JavaExample {
    public static void main(String[] args) {

    	byte num;

    	num = 113;
    	System.out.println(num);
    }
}

输出:

113

通过为变量num分配值 150 来尝试相同的程序,您将得到类型不匹配错误,因为值 150 超出了字节数据类型的范围。我上面提到的字节范围是-128 到 127。

短整数:

这在大小方面大于字节且小于整数。其范围是-32,768 到 32767。

此数据类型的默认大小:2 个字节

short num = 45678;

int :当short不足以容纳数字时使用,它有更宽的范围:-2,147,483,648 到 2,147,483,647

默认大小:4 字节

默认值:0

示例:

class JavaExample {
    public static void main(String[] args) {

    	short num;

    	num = 150;
    	System.out.println(num);
    }
}

输出:

150

字节数据类型不能保持值 150,但短数据类型可以因为它具有更宽的范围。

长整数:

int不足以容纳该值时使用,它具有比int数据类型更宽的范围,范围从-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。

大小:8 字节

默认值:0

示例:

class JavaExample {
    public static void main(String[] args) {

    	long num = -12332252626L;
    	System.out.println(num);
    }
}

输出:

-12332252626

double :足以容纳 15 位十进制数

大小:8 字节

示例:

class JavaExample {
    public static void main(String[] args) {

    	double num = -42937737.9d;
    	System.out.println(num);
    }
}

输出:

-4.29377379E7

float :足以容纳 6 到 7 个十进制数

大小:4 个字节

class JavaExample {
    public static void main(String[] args) {

    	float num = 19.98f;
    	System.out.println(num);
    }
}

输出:

19.98

boolean :保存truefalse

class JavaExample {
    public static void main(String[] args) {

    	boolean b = false;
    	System.out.println(b);
    }
}

输出:

false

char :持有字符。

大小:2 个字节

class JavaExample {
    public static void main(String[] args) {

    	char ch = 'Z';
    	System.out.println(ch);
    }
}

输出:

Z

Java 中的字面值

字面值是我们分配给程序中的变量的固定值。

int num=10;

这里的值 10 是整数字面值。

char ch = 'A';

这里A是一个字符字面值

整数字面值

整数字面值分配给数据类型byteshortintlong的变量。

byte b = 100;
short s = 200;
int num = 13313131;
long l = 928389283L;

浮点字面值

用于数据类型floatdouble

double num1 = 22.4;
float num2 = 22.4f;

注意:始终使用f后缀浮点值,否则编译器会将其视为double

字符和字符串字面值

用于charString类型。

char ch = 'Z';
String str = "BeginnersBook";

在继续下一个主题之前,查看这些基本 java 程序

  1. Java 程序:添加两个数字
  2. Java 程序:乘以两个数字
  3. Java 程序:读取数字(由用户输入)

Java 9 特性

Java 9 JShell(Java Shell) - REPL

原文: https://beginnersbook.com/2018/04/java-9-jshell-repl/

JShell 代表 java shell。它也被称为 REPL(读取求值打印循环)。这个工具的目的是提供一种学习 Java 的简单方法,但是如何?让我们看看它。我们知道我们必须编写几行代码才能在屏幕上打印内容,例如 - 要在屏幕上打印"Hello World",我们必须编写以下代码。

public class HelloWorld {

    public static void main(String[] args) {
        // Prints "Hello, World!" on the screen
        System.out.println("Hello, World!");
    }

}

如果你是一个 java 初学者,这段代码不容易掌握。为了以有趣和互动的方式学习 Java,Oracle 公司提出了一个名为 jshell 的工具。这些工具已经可以在其他流行的编程语言中使用,例如 Python ,Scala 等。

要在 JShell 中显示"Hello World",您只需编写:

jshell> System.out.println("Hello, World!")
Hello, World!

JShell - Java Shell Hello World

为什么要使用 JShell?

使用 jshell 的主要优点是,您可以在此处测试您的部分代码(单个语句,方法等),而无需编写完整的程序,然后检查各种可能的方案。一旦您对代码感到满意,就可以将它从 jshell 复制到主程序。

这允许您在不破坏主程序的情况下尝试多个场景,并让您有机会尝试学习

如何启动 JShell?

要启动 jshell,你必须在你的系统上安装 java 9,如果你有旧版本的 java,那么你可以通过从这里下载最新版本来升级它。

要启动 jshell,请打开命令提示符(如果您使用的是 Mac OS X,请打开终端),键入jshell并按Enter键。

如果你得到如下错误: jshell:找不到命令(如下面的屏幕截图所示),那么你必须设置系统的路径。

jshell Command Not Found

如果一切正常,那么你应该看到这样的屏幕。

Starting JShell on your System

如何退出 JShell?

要停止 JShell,请键入/exit并按Enter键。

How to stop exit JShell

有关 JShell 的更多教程

  1. JShell - 使用变量
  2. JShell - 使用方法

参考:

Oracle JDK 9 文档

Java 9 - 创建不可变List的工厂方法

原文: https://beginnersbook.com/2018/04/java-9-factory-method-to-create-immutable-list/

Java 9 到中引入了几种有用的工厂方法,创建了不可变(不可修改)List

1.在 Java 9 之前创建不可变List

在我们看到 Java 9 中引入的工厂方法之前。让我们看看我们在 Java 9 之前如何创建不可变List

1.1 在 java SE 9 之前创建空的不可变List

在 Java 9 之前,我们必须使用Collections类的unmodifiableList()方法来创建不可变List

List<String> noElementList = new ArrayList<String>();
List<String> immuList = Collections.unmodifiableList(noElementList);

注意:让我们测试 Java Shell(JShell)中的代码。

Creating immutable list before Java 9

1.2 在 Java SE 9 之前创建非空的不可变List

List<String> list = new ArrayList<String>();
list.add("Chaitanya");
list.add("Rick");
list.add("Glenn");
List<String> immuList = Collections.unmodifiableList(list);

Non Empty Immutable List Prior to Java SE 9

2. Java 9 - 使用List的静态工厂方法创建不可变List

Java 9 引入了of()方法的几个版本来创建不可修改的列表。

static <E> List<E> of()

2.1 Java 9 - 创建空的不可变List

List<String> immuList = List.of();

Java 9 Creating Empty List using of() method

2.2 Java 9 - 创建非空的不可变List

让我们采用与上面使用unmodifiableList()方法相同的示例。您可以看到在 Java 9 中创建此类列表是多么简单。我们使用List的工厂方法将 5 行代码减少到一行。

List<String> immuList = List.of("Chaitanya", "Rick", "Glenn");

Java 9 creating non empty immutable list

什么是不可变List

  1. 不可变List不允许添加,删除和更新其元素。
jshell> List<String> immuList = List.of("Chaitanya", "Rick", "Glenn");
immuList ==> [Chaitanya, Rick, Glenn]

jshell> immuList.add("Negan")
|  java.lang.UnsupportedOperationException thrown: 
|        at ImmutableCollections.uoe (ImmutableCollections.java:71)
|        at ImmutableCollections$AbstractImmutableList.add 
(ImmutableCollections.java:77)
|        at (#2:1)
  1. 我们不能将null元素添加到不可变List中。
jshell> List<String> immuList = List.of("Chaitanya", "Rick", "Glenn");
immuList ==> [Chaitanya, Rick, Glenn]

jshell> immuList.add(null)
|  java.lang.UnsupportedOperationException thrown: 
|        at ImmutableCollections.uoe (ImmutableCollections.java:71)
|        at ImmutableCollections$AbstractImmutableList.add 
(ImmutableCollections.java:77)
|        at (#2:1)

UnsupportedOperationException while adding null element to an immutable list

Java 9 - 创建不可变Set的工厂方法

原文: https://beginnersbook.com/2018/04/java-9-factory-methods-to-create-immutable-set/

在上一个教程中,我们学习了如何使用 Java 9 中引入的工厂方法轻松创建不可变List。在本指南中,我们将看到使用新引入的工厂方法来创建不可变Set

1.在 Java 9 之前创建不可变Set

在我们讨论如何使用工厂方法创建不可修改的Set之前,让我们看看我们如何在 Java 9 之前创建不可变Set

1.1 在 java SE 9 之前创建空的不可变Set

在 Java 9 之前,我们必须使用Collections类的unmodifiableSet()方法来创建不可变的Set。在以下示例中,我们将创建一个空集。

Set<String> emptyHashSet = new HashSet<String>();
Set<String> immutableHSet = Collections.unmodifiableSet(emptyHashSet);

让我们在 JShell (Java 9 中引入的新工具)中测试此代码

Creating Empty Set Before Java 9

1.2 在 Java SE 9 之前创建非空的不可变Set

这就是我们在 Java 9 之前用来创建非空的不可变Set的方法。正如您所看到的,我们必须编写几行代码来实现这一点。在 Java 9 中,我们可以在一行中编写此代码,我们将在下一节中看到。

Set<String> hset = new HashSet<String>();
hset.add("Jon Snow");
hset.add("Khal Drogo");
hset.add("Daenerys");
Set<String> immutableSet = Collections.unmodifiableSet(hset); 

Non Empty immutable Set before java 9

2. Java 9 - 使用Set的静态工厂方法创建不可变Set

Java 9 引入了几个版本的of()方法来创建不可修改的集合。

static <E> Set<E> of()

2.1 Java 9 - 创建空的不可变Set

Set<String> immutableSet = Set.of();

Creating Empty Set in Java 9 using Factory Methods

2.2 Java 9 - 创建非空的不可变Set

正如您所看到的,在 Java 9 中创建不可变Set是多么简单。

Set<String> immutableSet = Set.of("Apple", "Banana", "Orange");

Creating Non Empty Set in Java 9 using method of()

3.什么是不可变Set

  1. 不可变Set不允许添加,删除和更新其元素,如果我们尝试这样做,那么我们将得到UnsupportedOperationException异常。让我们举个例子来看看这个。
jshell> Set immutableSet = Set.of("Paul", "Lora", "Steve");
immutableSet ==> [Paul, Lora, Steve]

jshell> immutableSet.add("Harry")
|  java.lang.UnsupportedOperationException thrown: 
|        at ImmutableCollections.uoe (ImmutableCollections.java:71)
|        at ImmutableCollections$AbstractImmutableSet.add (ImmutableCollections.java:281)
|        at (#2:1)
  1. 我们不能将null元素添加到不可变Set
jshell> Set immutableSet = Set.of("Paul", "Lora", "Steve");
immutableSet ==> [Lora, Steve, Paul]

jshell> immutableSet.add(null)
|  java.lang.UnsupportedOperationException thrown: 
|        at ImmutableCollections.uoe (ImmutableCollections.java:71)
|        at ImmutableCollections$AbstractImmutableSet.add (ImmutableCollections.java:281)
|        at (#2:1)

Java 9 - 用于创建不可变Map的工厂方法

原文: https://beginnersbook.com/2018/04/java-9-factory-methods-to-create-immutable-map/

在之前的教程中,我们了解了使用 Java 9 中引入的工厂方法创建不可变List不可变Set。在本指南中,我们将学习**如何使用 Java 9 工厂方法创建不可变MapMap.Entry

1.在 Java 9 之前创建不可变Map

在我们看到如何使用 Java 9 工厂方法创建不可变Map之前,让我们看看我们在 Java 9 之前如何创建它们。

1.1 在 Java 9 之前创建空映射

我们曾经使用Collections类的unmodifiableMap()方法来创建不可修改的(不可变的)Map

Map<String,String> map = new HashMap<String, String>();
Map<String,String> immutableMap = Collections.unmodifiableMap(map);

让我们在 JShell 中测试一下。

Creating Empty Map before Java 9

1.2 在 Java 9 之前创建非空映射


Map<String,String> map = new HashMap<String, String>();
map.put("Key1", "Jon");
map.put("Key2", "Steve");
map.put("Key3", "Mia");
map.put("Key4", "Lora");
Map<String,String> immutableMap = Collections.unmodifiableMap(map);

让我们在 JShell 中测试它。

Java 9 Factory Methods to create immutable Map

2. Java 9 工厂方法创建不可变Map

Java 9 中引入了几种有用的工厂方法来创建不可修改的Map。我们将采用与上面相同的示例来比较 Java 9 中的事情变得更容易。在 Java 9 之前编写的代码行在工厂方法的帮助下大大减少了。

2.1 Java 9 - 使用Map.of()工厂方法的不可变空映射

我们用于空映射的方法:

static <K,V> Map<K,V> of()

示例:

Map<String,String> immutableMap = Map.of()

在 JShell 中测试:

jshell> Map<String,String> immutableMap = Map.of()
immutableMap ==> {}

2.2 Java 9 - 使用Map.of(Key, Value ...)工厂方法创建不可变非空映射

static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2...)

示例:

为了演示使用Map.of()工厂方法,我们采用了与上面使用unmodifiableMap()方法相同的示例。正如您所看到的,它在 Java 9 中是多么简单。我们在一行中减少了 6 行代码。

Map<String, String> immutableMap = 
Map.of("Key1", "Jon", "Key2", "Steve", "Key3", "Mia", "Key4", "Lora")

在 JShell 中测试:

jshell> Map<String, String> immutableMap = 
Map.of("Key1", "Jon", "Key2", "Steve", "Key3", "Mia", "Key4", "Lora")
immutableMap ==> {Key3=Mia, Key4=Lora, Key1=Jon, Key2=Steve}

什么是不可变的映射?

  1. 不可变Map不允许添加,删除和更新其元素。如果您尝试执行这些操作,程序将抛出UnsupportedOperationException
jshell> Map<String, String> immutableMap = 
Map.of("Key1", "Jon", "Key2", "Steve", "Key3", "Mia", "Key4", "Lora")
immutableMap ==> {Key3=Mia, Key4=Lora, Key1=Jon, Key2=Steve}

jshell> immutableMap.put("Key5", "Chaitanya");
| java.lang.UnsupportedOperationException thrown:
| at ImmutableCollections.uoe (ImmutableCollections.java:71)
| at ImmutableCollections$AbstractImmutableMap.put 
(ImmutableCollections.java:558)
| at (#2:1)
  1. 他们不允许null元素。添加null元素会抛出相同的UnsupportedOperationException

Java 9 - 接口中的私有方法

原文: https://beginnersbook.com/2018/05/java-9-private-methods-in-interfaces-with-examples/

我们知道 Java 8 允许我们在接口中创建默认和静态方法。目的是在接口上添加新方法,而不会破坏已经实现这些接口的类。Java 9 引入了另一个新功能,Java 9 SE 以后我们可以在接口中拥有私有方法。在本指南中,我们将了解为什么他们添加了此功能,它的用途以及如何使用它。

为什么 Java 9 允许我们在接口中使用私有方法?

Java 9 在接口中引入了私有方法,通过私有方法共享多个默认方法的公共代码来删除冗余。

为了理解这一点,我们必须在 Java 8 中使用一个示例(没有私有方法),然后我们将使用 Java 9(使用私有方法)采用相同的示例。

Java 8 中的示例 - 具有重复代码的多个默认方法(公共代码)

在此示例中,我们将看到默认方法如何具有重复代码,这些代码不必要地增加代码行并使代码更少 - 可读。我们将使用私有方法再次使用相同的示例来查看私有方法如何帮助我们避免重复的代码。

interface MyInterfaceInJava8 {
   default void method1() {
	System.out.println("Starting method");
	System.out.println("Doing someting");
	System.out.println("This is method1");
   }
   default void method2() {
	System.out.println("Starting method");
	System.out.println("Doing someting");
	System.out.println("This is method2");
   }
}
public class JavaExample implements MyInterfaceInJava8{
   public static void main(String args[]) {
	JavaExample je = new JavaExample();
	je.method1();
	je.method2();
   }
}

输出:

Java 8 - default methods with common code

Java 9 中的示例 - 使用私有方法共享公共代码的默认方法

我们采用的是上面我们看到的相同示例。这次我们将介绍一种私有方法来共享公共代码。

interface MyInterfaceInJava9 {
   default void method1() {
	//calling private method
	printLines();
	System.out.println("This is method1");
   }
   default void method2() {
	//calling private method
	printLines();
	System.out.println("This is method2");
   }
   private void printLines() {
	System.out.println("Starting method");
	System.out.println("Doing someting");
   }
}
public class JavaExample implements MyInterfaceInJava9{
   public static void main(String args[]) {
	JavaExample je = new JavaExample();
	je.method1();
	je.method2();
   }
}

输出:

Starting method
Doing someting
This is method1
Starting method
Doing someting
This is method2

如您所见,输出相同,代码大小已减少。

基于此,我们可以说在接口中拥有私有方法的优点是:

  1. 允许默认方法共享公共代码以避免重复代码(冗余)
  2. 提高代码可读性。

Java 9 - 私有静态方法

到目前为止,我们已经倾向于如何在接口中使用私有方法来共享默认方法的公共代码。 Java 9 还允许我们在接口中使用私有静态方法

从 java 8 开始,我们可以在接口中使用静态方法和默认方法。我们不能使用非静态私有方法共享静态方法的公共代码,我们必须使用私有静态方法来做到这一点。

让我们举一个例子来理解这一点。

interface MyInterfaceInJava9 {
   static void method1() {
	//calling private method
	printLines();
	System.out.println("This is method1");
   }
   static void method2() {
	//calling private method
	printLines();
	System.out.println("This is method2");
   }
   //this must be static else we will get compilation error
   private static void printLines() {
	System.out.println("Starting method");
	System.out.println("Doing someting");
   }
   default void mymethods() {
	method1();
	method2();
   }
}
public class JavaExample implements MyInterfaceInJava9{
   public static void main(String args[]) {
	JavaExample je = new JavaExample();
	je.mymethods();
   }
}

输出:

Starting method
Doing someting
This is method1
Starting method
Doing someting
This is method2

这是 Eclipse Oxygen 的截图。

Java 9 private methods in interface

Java 9 - try-with-resource改进

原文: https://beginnersbook.com/2018/05/java-9-try-with-resources-enhancements/

try-with-resource语句首先在 Java 7 中引入。该语句在 Java 9 中得到了重大改进。在本指南中,我们将讨论 Java 9 中try-with-resource语句的改进

什么是try-with-resource

这个语句最初是在 Java 7 中引入的,以避免我们为异常处理编写的冗余代码。这句话的优点是:

  1. 尝试用资源自动关闭所有资源(文件,数据库连接,网络连接等)。无需明确关闭它们。这可以防止内存泄漏。
  2. 借助try-with-resource,我们可以减少不必要的代码行,使代码更具可读性。

我们以前如何使用try-with-resource在 Java 7 中编写代码?

这就是我们在 Java 7 中使用Try-With-Resource语句的方式。

import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
public class JavaExample {  
    public static void main(String[] args) throws FileNotFoundException {  
        try(FileOutputStream fileOutputStream = new FileOutputStream("beginnersbook.txt");){ 
             //We are writing this string in the output file using FileOutputStream
             String mystring = "We are writing this line in the output file."; 

             //Converting the given string in bytes
             byte bytes[] = mystring.getBytes();       

             //Writing the bytes into the file
             fileOutputStream.write(bytes);      

             //Displaying success message after the successful write operation
             System.out.println("The given String is written in the file successfully");           
        }catch(Exception e) {  
            System.out.println(e);  
        }         
    }  
}

输出:

The given String is written in the file successfully

Java 7 中的try-with-resource问题

Java 7 中的Try-With-Resource语句存在某些问题。此语句不允许在语句块(范围)之外声明资源。让我们举一个例子来理解这一点。

Java 7 - 在 Try-With-Resources 块之外声明的资源

import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
public class JavaExample {  
    public static void main(String[] args) throws FileNotFoundException { 
       FileOutputStream fileOutputStream = new FileOutputStream("beginnersbook.txt");
       try(fileOutputStream){ 
            String mystring = "We are writing this line in the output file."; 
            byte bytes[] = mystring.getBytes();       
            fileOutputStream.write(bytes);      
            System.out.println("The given String is written in the file successfully");           
        }catch(Exception e) {  
            System.out.println(e);  
        }         
    }  
}

Java 7 中的输出:

Compile-time error

上面的示例抛出编译时错误,因为资源是在Try-With-Resource语句的范围之外声明的。

Java 7 - 外部声明的资源 - 重复资源作为变通方法

为了解决上述错误,我们不得不在 Java 7 中做一个解决方法。我们过去常常复制资源引用,如下所示:

import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
public class JavaExample {  
    public static void main(String[] args) throws FileNotFoundException { 
       FileOutputStream fileOutputStream = new FileOutputStream("beginnersbook.txt");
       try(FileOutputStream fileOutputStream2 = fileOutputStream){ 
            String mystring = "We are writing this line in the output file."; 
            byte bytes[] = mystring.getBytes();       
            fileOutputStream2.write(bytes);      
            System.out.println("The given String is written in the file successfully");           
        }catch(Exception e) {  
            System.out.println(e);  
        }         
    }  
}

这段代码在 Java 7 中运行良好。
注意try块中的FileOutputStream fileOutputStream2 = fileOutputStream行。我们在Try-With-Resource的范围内创建了对已声明的输出流的另一个引用。

Java 9 - try-with-resource改进

Java 9 为传统的Try-With-Resource语句提供了一个重要的改进。 Java 9 允许我们在 Try-With-Resource之外声明资源。我们不再需要创建局部变量来访问资源。让我们采用与 Java 7 相同的示例,但遇到了编译错误。在 Java 9 中,此代码运行得非常好。

import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
public class JavaExample {  
    public static void main(String[] args) throws FileNotFoundException { 
       FileOutputStream fileOutputStream = new FileOutputStream("beginnersbook.txt");
       try(fileOutputStream){ 
            String mystring = "We are writing this line in the output file."; 
            byte bytes[] = mystring.getBytes();       
            fileOutputStream.write(bytes);      
            System.out.println("The given String is written in the file successfully");           
        }catch(Exception e) {  
            System.out.println(e);  
        }         
    }  
}

输出:

The given String is written in the file successfully

Eclipse Oxygen 运行 Java SE 9 中此代码的屏幕截图。

Java 9 - Try with resources enhancements

Java 9 - 匿名内部类和菱形运算符

原文: https://beginnersbook.com/2018/05/java-9-anonymous-inner-classes-and-diamond-operator/

在这篇文章中,我们将讨论 Java SE 9 中引入的菱形运算符增强

什么是菱形运算符?

菱形操作符是作为 java SE 7 中的新功能引入的。菱形操作符的目的是通过将泛型类型保留在表达式的右侧来避免冗余代码。

// This is before Java 7\. We have to explicitly mention generic type 
// in the right side as well. 
List<String> myList = new ArrayList<String>();

// Since Java 7, no need to mention generic type in the right side
// instead we can use diamond operator. Compiler can infer type.
List<String> myList = new ArrayList<>();

使用匿名内部类时菱形运算符的问题

Java 7 允许我们在普通类中使用菱形运算符,但它不允许我们在匿名内部类中使用它们。让我们举一个例子:

abstract class MyClass<T>{  
    abstract T add(T num, T num2);  
}  
public class JavaExample {  
    public static void main(String[] args) {  
        MyClass<Integer> obj = new MyClass<>() {  
            Integer add(Integer x, Integer y) {  
                return x+y;   
            }  
        };    
        Integer sum = obj.add(100,101);  
        System.out.println(sum);  
    }  
}

输出:

$javac JavaExample.java
JavaExample.java:7: error: cannot infer type arguments for MyClass
        MyClass obj = new MyClass<>() {  
                                        ^
  reason: cannot use '<>' with anonymous inner classes
  where T is a type-variable:
    T extends Object declared in class MyClass
1 error

当我们在 Java SE 8 中运行上面的代码时,我们遇到了编译错误。

Java 9 - 菱形运算符改进

Java 9 改进了菱形运算符的使用,并允许我们将菱形运算符与匿名内部类一起使用。让我们采用我们在上面看到的相同的例子。
在 Java SE 9 中运行此代码

abstract class MyClass<T>{  
    abstract T add(T num, T num2);  
}  
public class JavaExample {  
    public static void main(String[] args) {  
        MyClass<Integer> obj = new MyClass<>() {  
            Integer add(Integer x, Integer y) {  
                return x+y;   
            }  
        };    
        Integer sum = obj.add(100,101);  
        System.out.println(sum);  
    }  
}

输出:

201

Eclipse Oxygen 中使用 jdk 9

java 9 Diamond operator enhancements的上述代码的屏幕截图

Java 9 - @SafeVarargs注解

原文: https://beginnersbook.com/2018/05/java-9-safevarargs-annotation/

Java 7 引入了@SafeVarargs注解来抑制当方法具有varargs(可变数量的参数)时出现的不安全操作警告。@SafeVarargs注解只能用于无法覆盖的方法(最终或静态方法或构造函数),因为重写方法仍然可以对其 varargs(可变数量的参数)执行不安全操作。

Java 9 扩展了@SafeVarargs注解的使用,它现在也可以与私有方法一起使用。这是因为无法覆盖私有方法。之前这个注解仅限于最终或静态方法或构造函数,但现在它可以与私有方法一起使用。

Java 9 示例 - 当我们不使用@SafeVarargs注解时?

import java.util.ArrayList;  
import java.util.List;  
public class JavaExample{  
    // We are not using @SafeVarargs annotation - Java 9
    private void print(List... names) {  
        for (List<String> name : names) {  
            System.out.println(name);  
        }  
    }  
    public static void main(String[] args) {  
        JavaExample obj = new JavaExample();  
        List<String> list = new ArrayList<String>();  
        list.add("Kevin");  
        list.add("Rick"); 
        list.add("Negan");
        obj.print(list);  
    }     
}

警告:

Type safety: Potential heap pollution via varargs parameter names
Type safety: A generic array of List is created for a varargs 
 parameter

输出:

[Kevin, Rick, Negan]

正如你所看到的,代码运行良好但却没有产生任何警告。

Eclipse Oxygen IDE 中此代码的屏幕截图,显示警告。

Java 9 @SafeVarargs annotation

Java 9 - @SafeVarargs注解示例

让我们在使用@SafeVarargs注解后再次运行相同的代码。

import java.util.ArrayList;  
import java.util.List;  
public class JavaExample{  
    @SafeVarargs
    private void print(List... names) {  
        for (List<String> name : names) {  
            System.out.println(name);  
        }  
    }  
    public static void main(String[] args) {  
        JavaExample obj = new JavaExample();  
        List<String> list = new ArrayList<String>();  
        list.add("Kevin");  
        list.add("Rick"); 
        list.add("Negan");
        obj.print(list);  
    }      
}

相同的输出没有任何警告。

注意:如果您尝试在 Java 7 和 Java 8 中编译上述代码,您将收到编译错误,因为此增强功能在 Java 9 中完成,在 Java 9 之前 - 不允许使用私有方法标有此注解。

Java 9 - 流 API 改进

原文: https://beginnersbook.com/2018/06/java-9-stream-api-enhancements/

我们已经了解到 Java 8 引入了流 API 以及其他一些很酷的功能。如果您不熟悉流,请参阅本指南:Java 8 - 流 API。Java 9 为流 API 引入了四种新方法。这些方法添加在java.util.Stream接口中。

Java 9 - 流 API 改进

Java 9 在流中添加了以下四种方法。由于Stream是一个接口,添加到它的方法是默认的和静态的。这是因为 java 8 允许我们在接口中使用默认和静态方法

  1. dropWhile() - 默认方法
  2. takeWhile() - 默认方法
  3. iterate() - 静态方法
  4. ofNullable() - 静态方法

Java 9 - Stream dropWhile()方法

方法dropWhile()删除流的所有元素,直到给定的谓词失败。

例如:

jshell> Stream<Integer> mystream = Stream.of(11, 22, 40, 60, 100)
mystream ==> [email protected]

jshell> mystream.dropWhile(num -> num < 50).forEach(num -> System.out.println(num))
60
100

Java 9 Stream doWhile() method

注意:当流是无序的时,dropWhile()会丢弃所​​有元素,直到给定的谓词失败,一旦谓词失败,此方法不会检查流的其他元素。这意味着结果流可能具有与谓词匹配的元素,让我们举一个例子来理解这一点。

jshell> Stream<Integer> mystream = Stream.of(11, 22, 40, 60, 10, 15, 30, 100)
mystream ==> [email protected]

jshell> mystream.dropWhile(num -> num < 50).forEach(num -> System.out.println(num))
60
10
15
30
100

这里,元素 60 之后的流中存在的元素10,15,30与谓词匹配但是它们没有被dropWhile()删除,因为当谓词在元素 60 上失败时,该方法没有检查其他元素。

Java 9 dropWhile() method unordered stream

Java 9 - Stream takeWhile()方法

方法takeWhile()dropWhile()方法相反。此方法在结果流中获取流的所有元素,直到谓词失败。简而言之,当谓词失败时,它会丢弃该元素以及流中该元素之后的所有元素。让我们举几个例子来理解这一点。

这里对有序流使用takeWhile()方法对流进行排序,takeWhile()方法获取所有元素,直到谓词在元素值 60 处失败。

jshell> Stream<Integer> mystream = Stream.of(10, 20, 30, 40, 60, 90, 120)
mystream ==> [email protected]

jshell> mystream.takeWhile(num -> num < 50).forEach(num -> System.out.println(num))
10
20
30
40

对无序流使用takeWhile()方法与dropWhile()方法类似,一旦谓词失败,此方法也不会进一步检查元素。这就是元素 10 和 15 被丢弃的原因,因为它们位于元素 60 之后(谓词失败)。

jshell> Stream<Integer> mystream = Stream.of(10, 20, 40, 60, 70, 10, 15, 100)
mystream ==> [email protected]

jshell> mystream.takeWhile(num -> num < 50).forEach(num -> System.out.println(num))
10
20
40

Java 9 - Stream iterate()方法

Java 9 中的iterate方法有三个参数。
第一个参数是初始化值,返回的流以此值开始。
第二个参数是谓词,迭代继续,直到这个给定的谓词返回false
第三个参数更新上一次迭代的值。

示例:在此示例中,第一个参数为 1.流以元素 1 开头。

num -> num < 30是第二个参数,它是一个谓词。迭代继续,直到返回falsenum -> num * 3是更新从上一次迭代返回的值的第三个参数。这类似于循环的计数器变量。

jshell> IntStream.iterate(1, num -> num < 30, num -> num*3).forEach(num ->System.out.println(num))
1
3
9
27

从值 1 开始,我们将返回值乘以 3,这将一直持续到返回值大于 30。

Java 9 Iterate method

Java 9 - Stream ofNullable()方法

引入此方法是为了避免NullPointerException。如果流为null,则此方法返回空流。它也可以在非空流上使用,返回单个元素的顺序流。

空流示例

jshell> Stream<String> stream = Stream.ofNullable(null)
stream ==> [email protected]

jshell> stream.forEach(str -> System.out.println(str))

jshell>

Java 9 ofNullable() method

非空流示例

jshell> Stream<String> stream = Stream.ofNullable("Rose")
stream ==> [email protected]

jshell> stream.forEach(str -> System.out.println(str))
Rose

jshell>

这些是在 Java SE 9 中完成的四种流 API 增强功能。

Java 中的运算符

原文: https://beginnersbook.com/2017/08/operators-in-java/

运算符是表示动作的字符,例如+是表示加法的算术运算符。

Java 中的运算符类型

1)基本算术运算符

2)赋值运算符

3)自增和自减运算符

4)逻辑运算符

5)比较(关系)运算符

6)位运算符

7)三元运算符

1)基本算术运算符

基本算术运算符是:+, - ,*,/,%

  • +用于加法。

  • -用于减法。

  • *用于乘法。

  • /用于除法。

  • %用于模数。

注意:模运算符返回余数,例如10%5将返回 0。

算术运算符的例子

public class ArithmeticOperatorDemo {
   public static void main(String args[]) {
      int num1 = 100;
      int num2 = 20;

      System.out.println("num1 + num2: " + (num1 + num2) );
      System.out.println("num1 - num2: " + (num1 - num2) );
      System.out.println("num1 * num2: " + (num1 * num2) ); 
      System.out.println("num1 / num2: " + (num1 / num2) );
      System.out.println("num1 % num2: " + (num1 % num2) );
   }
}

输出:

num1 + num2: 120
num1 - num2: 80
num1 * num2: 2000
num1 / num2: 5
num1 % num2: 0

查看与 Java 中的算术运算符相关的这些 java 程序

  1. Java 程序:添加两个数字
  2. Java 程序:乘以两个数字

2)赋值运算符

java 中的赋值运算符是:=,+ =, - =,* =,/ =,%=

  • num2 = num1将变量num1的值赋给变量

  • num2 + = num1等于num2 = num2 + num1

  • num2- = num1等于num2 = num2-num1

  • num2 * = num1等于num2 = num2 * num1

  • num2 / = num1等于num2 = num2 / num1

  • num2%= num1等于num2 = num2%num1

赋值运算符的示例

public class AssignmentOperatorDemo {
   public static void main(String args[]) {
      int num1 = 10;
      int num2 = 20;

      num2 = num1;
      System.out.println("= 输出: "+num2);

      num2 += num1;
      System.out.println("+= 输出: "+num2);

      num2 -= num1;
      System.out.println("-= 输出: "+num2);

      num2 *= num1;
      System.out.println("*= 输出: "+num2);

      num2 /= num1;
      System.out.println("/= 输出: "+num2);

      num2 %= num1;
      System.out.println("%= 输出: "+num2);
   }
}

输出:

= 输出: 10
+= 输出: 20
-= 输出: 10
*= 输出: 100
/= 输出: 10
%= 输出: 0

3)自增和自减运算符

++--

  • num++相当于num=num+1;

  • num--相当于num=num-1;

自增和自减运算符的示例

public class AutoOperatorDemo {
   public static void main(String args[]){
      int num1=100;
      int num2=200;
      num1++;
      num2--;
      System.out.println("num1++ is: "+num1);
      System.out.println("num2-- is: "+num2);
   }
}

输出:

num1++ is: 101
num2-- is: 199

4)逻辑运算符

逻辑运算符与二进制变量一起使用。它们主要用于条件语句和循环以求值条件。

java 中的逻辑运算符是:&& || !

假设我们有两个布尔变量b1b2

如果b1b2都为真,则b1 && b2将返回true,否则它将返回false

如果b1b2都为假,则*b1 || b2将返回false,否则返回true

!b1将返回与b1相反的位置,这意味着如果b1为假则为真,如果b1为真则返回false

逻辑运算符的示例

public class LogicalOperatorDemo {
   public static void main(String args[]) {
      boolean b1 = true;
      boolean b2 = false;

      System.out.println("b1 && b2: " + (b1&&b2));
      System.out.println("b1 || b2: " + (b1||b2));
      System.out.println("!(b1 && b2): " + !(b1&&b2));
   }
}

输出:

b1 && b2: false
b1 || b2: true
!(b1 && b2): true

5)比较(关系)运算符

我们在 Java 中有六个关系运算符:== != > < >= <=

  • 如果左侧和右侧都相等,则==返回true

  • 如果左侧不等于运算符的右侧,则!=返回true

  • 如果左侧大于右侧,>返回true

  • 如果左侧小于右侧,<返回true

  • 如果左侧大于或等于右侧,则>=返回true

  • 如果左侧小于或等于右侧,则<=返回true

关系运算符的示例

注意:这个例子使用if-else语句,这是我们的下一个教程,如果你发现它很难理解,那么在 Java 中引用if-else

public class RelationalOperatorDemo {
   public static void main(String args[]) {
      int num1 = 10;
      int num2 = 50;
      if (num1==num2) {
	 System.out.println("num1 and num2 are equal");
      }
      else{
	 System.out.println("num1 and num2 are not equal");
      }

      if( num1 != num2 ){
	 System.out.println("num1 and num2 are not equal");
      }
      else{
	 System.out.println("num1 and num2 are equal");
      }

      if( num1 > num2 ){
	 System.out.println("num1 is greater than num2");
      }
      else{
	 System.out.println("num1 is not greater than num2");
      }

      if( num1 >= num2 ){
	 System.out.println("num1 is greater than or equal to num2");
      }
      else{
	 System.out.println("num1 is less than num2");
      }

      if( num1 < num2 ){
	 System.out.println("num1 is less than num2");
      }
      else{
	 System.out.println("num1 is not less than num2");
      }

      if( num1 <= num2){
	 System.out.println("num1 is less than or equal to num2");
      }
      else{
	 System.out.println("num1 is greater than num2");
      }
   }
}

输出:

num1 and num2 are not equal
num1 and num2 are not equal
num1 is not greater than num2
num1 is less than num2
num1 is less than num2
num1 is less than or equal to num2

看看这些与关系运算符相关的 java 程序:

  1. Java 程序:检查数字是正数还是负数
  2. Java 程序:检查数字是偶数还是奇数

6)按位运算符

有六个按位运算符:& | ^ ~ << >>

num1 = 11; /*等于 00001011 */
num2 = 22; /*等于 00010110 */

按位运算符执行逐位处理。
num1 & num2比较num1num2的相应位,如果两个位相等则生成 1,否则返回 0.在我们的例子中它将返回:2,这是00000010,因为在num1num2的二进制形式中,只有倒数第二位是匹配。

num1 | num2比较num1num2的相应位,如果任一位为 1,则生成 1,否则返回 0.在我们的例子中,它将返回 31,即00011111

num1 ^ num2比较num1num2的相应位,如果它们不相等则生成 1,否则返回 0.在我们的例子中它将返回 29,相当于00011101

~num1是一个补码运算符,只是将位从 0 更改为 1,1 更改为 0.在我们的示例中,它将返回-12,其中 8 位等效于11110100

num1 << 2是左移位运算符,它将位移到左边,丢弃最左边的位,并将最右边的位赋值为 0.在我们的例子中,输出为 44,相当于00101100

注意:在下面的示例中,我们在此移位运算符的右侧提供 2,这是位向左移动两个位置的原因。我们可以更改此数字,并且位将按运算符右侧指定的位数移动。同样适用于右侧运算符。

num1 >> 2是右移位运算符,它将位向右移动,丢弃最右位,并将最左边的位指定为 0.在我们的例子中,输出为 2,相当00000010

按位运算符的示例

public class BitwiseOperatorDemo {
  public static void main(String args[]) {

     int num1 = 11;  /* 11 = 00001011 */
     int num2 = 22;  /* 22 = 00010110 */
     int result = 0;

     result = num1 & num2;   
     System.out.println("num1 & num2: "+result);

     result = num1 | num2;   
     System.out.println("num1 | num2: "+result);

     result = num1 ^ num2;   
     System.out.println("num1 ^ num2: "+result);

     result = ~num1;   
     System.out.println("~num1: "+result);

     result = num1 << 2;   
     System.out.println("num1 << 2: "+result); result = num1 >> 2;   
     System.out.println("num1 >> 2: "+result);
  }
}

输出:

num1 & num2: 2
num1 | num2: 31
num1 ^ num2: 29
~num1: -12
num1 << 2: 44 num1 >> 2: 2

查看这个程序: Java 程序:使用按位运算符交换两个数字

7)三元运算符

此运算符计算布尔表达式并根据结果分配值。

语法:

variable num1 = (expression) ? value if true : value if false

如果表达式结果为true,则将冒号(:)之前的第一个值分配给变量num1,否则将第二个值分配给num1

三元运算符的例子

public class TernaryOperatorDemo {

   public static void main(String args[]) {
        int num1, num2;
        num1 = 25;
        /* num1 is not equal to 10 that's why
	 * the second value after colon is assigned
	 * to the variable num2
	 */
	num2 = (num1 == 10) ? 100: 200;
	System.out.println( "num2: "+num2);

	/* num1 is equal to 25 that's why
	 * the first value is assigned
	 * to the variable num2
	 */
	num2 = (num1 == 25) ? 100: 200;
	System.out.println( "num2: "+num2);
   }
}

输出:

num2: 200
num2: 100

看看这些相关的 java 程序:

  1. 使用三元运算符查找三个数字中最大的 Java 程序
  2. Java 程序:使用三元运算符查找三个最小的数字

Java 中的运算符优先级

如果表达式具有多个运算符,则确定首先需要对哪个运算符求值。操作符在顶部具有较高优先级,在底部具有较低优先级。

一元运算符

++ -- ! ~

乘法

* / %

加法

+ -

移位

<< >> >>>

关系
< <= > >=

相等

== !=

位 AND

&

位异或

^

位 OR

|

逻辑 AND

&&

逻辑 OR

||

三元

?:

赋值

= += -= *= /= %= >>= <<= &= ^= |=

在 15 分钟内学习 Java 9 模块

原文: https://beginnersbook.com/2018/09/java-9-modules/

在本文中,我们将学习 Java 9 最重要的特性 - Java 9 模块。我们将涵盖所有内容,例如我们需要模块的原因,什么是模块,如何在 Java 中创建和使用模块。让我们开始吧。

关于模块的小背景

Java 模块系统是 Java 早就应该实现的。模块系统是 Jigsaw 计划的一部分,最初计划在 Java SE 7 中发布但延迟并推迟到 Java SE 8 发布,但又因为它是一个巨大而有声望的项目,它已经从 Java SE8 推迟发布并最终在 Java SE 9 中发布。**

在下一节中,我将从基础知识中进行解释,以便每个人都能理解模块的需求。您可能会发现它是一个巨大的文本墙,但我向您保证,如果您仔细阅读它,您将能够理解模块背后的原因和想法。

为什么我们需要 Java 中的模块?

java 最重要的特性是可重用性,它允许我们在继承接口的帮助下重用我们创建的类。我们可以在继承的帮助下继承类的行为,并且可以使用接口继承抽象。

为了有效地重用这些类,java 将它们分组在中,并且它以这样的方式完成,以便类似类型的类在单个包中。对于例如当我们处理集合时,我们需要的大多数类和接口都在java.util包中。

随着我们的代码大小的增加,java 中的包也增加了。想象一下,使用数百个包来处理一个非常大的程序,在这种情况下,很难理解哪些类正在使用什么。包是组织类的好方法,但我们需要在代码中使用包时组织它们

此外,使类可以在包之间重用的唯一方法是将其公开,当我们公开它时,任何人都可以使用它。这也需要解决。

Java 9 引入了一个很酷的新功能来解决我们上面讨论过的问题。它引入了一个名为“模块”的新功能。 “模块是一组包”。模块不仅可以组织包,还可以处理可访问性,以便可以使用我们想要重用的模块的部分,以及我们不想重用的部分,不能重复使用。

直到现在我们在理论上讨论了所有内容,让我们在代码和图表的帮助下讨论模块。

什么是 Java 9 模块?

模块是一组包。我们在一个模块中有两种类型的包 - 1)导出的包 2)隐藏的包。

导出的软件包:这些软件包旨在在模块外部使用,这意味着任何其他模块中的任何程序都可以使用这些软件包。

隐藏包:这些包不在外面使用,它们在模块内部,只能在模块内部使用。

Java 9 Module

为了进一步理解导出包和隐藏包的概念,我们举一个java.base模块的例子。

让我们借助下图了解这一点。在下图中,java.base中的绿色块表示“导出的包”,有几个导出的包,但我只提到了其中的一些。绿色块中提到的这些包可以由外部类使用。

黄色块中的包装是隐藏包装,在模块外部无法访问。这些包仅用于模块内部。

在 java 中我们在module-info.java文件中定义模块,并提及任何包作为导出包我们在export关键字之后提到包的名称,如右侧所示(灰色)在下图中阻止。

Module in Java 9 - exported packages & concealed packages

让我们在 Eclipse IDE 中编写程序并使用模块的概念。

Java 9 模块 - 在 Eclipse IDE 中创建和使用模块

我们将在一个模块中创建一个类,并在另一个模块中使用该类。

1.创建 Java 项目

New Project in Java 9

我们正在 Eclipse IDE 中创建一个 java 项目。我们使用的项目名称是beginnersbook.demo

2.创建module-info.java文件

创建项目后,右键单击项目名称,转到Configure选项,然后单击create module-info.java选项如下截图所示。给出与项目名称beginnersbook.demo相同的模块名称。

Create module-info.java file in Eclipse IDE

现在请将文件留空。我们稍后会来。

3.创建包和类

我们在这个模块中创建了一个类,我们将在另一个模块中使用这个类。我们正在beginnersbook.demo包中创建一个类BeginnersBook

BeginnersBook类的源代码:

package beginnersbook.demo;

public class BeginnersBook {
   public String welcomeMessage() {
      return "Welcome to BeginnersBook";
   }

}

4.导出我们创建的包

由于我们计划在另一个模块中使用BeginnersBook类,因此我们可以将此包导出,以便可以在模块外部使用。为此,请在module-info.java文件:

module-info.java file中编写此代码

module beginnersbook.demo {
   exports beginnersbook.demo;
}

项目beginnersbook.demo的最终结构如下:

Project Structure in Java 9 after creating module-info.java file

5.让我们创建另一个模块

让我们创建一个新的 java 项目,将其命名为beginnersbook.user,并按照我们上面创建的方式创建module-info.java文件,并将其命名为beginnersbook.user。该项目的module-info.java文件包含以下代码:

//module-info.java file
module beginnersbook.user {
   exports beginnersbook.user;
   requires beginnersbook.demo;
}

由于我们计划使用beginnersbook.demo包,因此我们在require关键字后面提到了包名,如上面的代码所示。

注意:您很可能会收到一条错误消息,指出包名称未解析,这是因为其他模块不在构建路径中。要解决此错误,请右键单击项目beginnersbook.user,转到Build Path -> 配置构建路径,如以下屏幕截图所示:

Build Path - Modules in Java9

转到Projects选项卡,单击Modulepath,然后单击右侧的Add按钮,添加我们创建的上一个 java 项目beginnersbook.demo。你的最终屏幕应如下所示:

Add Project to the Module Path - Java 9 Eclipse Oxygen

您的错误现在应该得到解决。

6.让我们在第二个模块中创建一个类

在包含beginnersbook.user包的项目中创建一个类BeginnersBookUser

BeginnersBookUser类的源代码:

package beginnersbook.user;
import beginnersbook.demo.BeginnersBook;
public class BeginnersBookUser {
   public static void main (String arg[]) {
      BeginnersBook obj = new BeginnersBook();
      System.out.println(obj.welcomeMessage());
   }
}

两个项目的最终结构如下:

Java 9 Module Final project structure

7.最后一步

让我们运行BeginnersBookUser类,你应该得到以下输出:

Welcome to BeginnersBook

多数民众赞成。为了简要说明,我们在模块中创建了一个类并导出了包,以便可以在模块外部使用该类。
为了测试它,我们在另一个模块中创建了另一个类,并使用了导出的包,最后使用了我们首先创建的类。

Java 中的ifif-else语句

原文: https://beginnersbook.com/2017/08/if-else-statement-in-java/

当我们需要根据条件执行一组语句时,我们需要使用控制流语句。例如,如果一个数字大于零,那么我们要打印“正数”,但如果它小于零,那么我们要打印“负数”。在这种情况下,程序中有两个print语句,但根据输入值一次只执行一个print语句。我们将看到如何使用控制语句在 java 程序中编写这种类型的条件。

在本教程中,我们将看到四种类型的控制语句,您可以根据需求在 java 程序中使用:在本教程中,我们将介绍以下条件语句:

a)if语句

b)嵌套if语句

c)if-else语句

d)if-else-if语句

if语句

if语句包含条件,后跟语句或一组语句,如下所示:

if(condition){
  Statement(s);
}

只有在给定条件为真时才会执行语句。如果条件为false,那么if语句体内的语句将被完全忽略。

if statement flow diagram

if语句的示例

public class IfStatementExample {

   public static void main(String args[]){
      int num=70;
      if( num < 100 ){
	  /* This println statement will only execute,
	   * if the above condition is true
	   */
	  System.out.println("number is less than 100");
      }
   }
}

输出:

number is less than 100

Java 中的嵌套if语句

当在另一个if语句中有if语句时,它被称为嵌套if语句

嵌套的结构如下所示:

if(condition_1) {
   Statement1(s);

   if(condition_2) {
      Statement2(s);
   }
}

如果condition_1true,则执行Statement1。只有条件(condition_1condition_2)都为真时,Statement2才会执行。

嵌套if语句的示例

public class NestedIfExample {

   public static void main(String args[]){
        int num=70;
	if( num < 100 ){ 
           System.out.println("number is less than 100"); 
           if(num > 50){
	      System.out.println("number is greater than 50");
	   }
	}
   }
}

输出:

number is less than 100
number is greater than 50

在 Java 中使用if-else语句

这是if-else语句的外观:

if(condition) {
   Statement(s);
}
else {
   Statement(s);
}

如果条件为真,则if内的语句将执行,如果条件为假,则else内的语句将执行。

If else flow diagram

if-else语句的示例

public class IfElseExample {

   public static void main(String args[]){
     int num=120;
     if( num < 50 ){
	System.out.println("num is less than 50");
     }
     else {
	System.out.println("num is greater than or equal 50");
     }
   }
}

输出:

num is greater than or equal 50

if-else-if语句

当我们需要检查多个条件时使用if-else-if语句。在这个声明中,我们只有一个if和一个else,但是我们可以有多个else if。它也被称为if else if梯子。这是它的样子:

if(condition_1) {
   /*if condition_1 is true execute this*/
   statement(s);
}
else if(condition_2) {
   /* execute this if condition_1 is not met and
    * condition_2 is met
    */
   statement(s);
}
else if(condition_3) {
   /* execute this if condition_1 & condition_2 are
    * not met and condition_3 is met
    */
   statement(s);
}
.
.
.
else {
   /* if none of the condition is true
    * then these statements gets executed
    */
   statement(s);
}

注意:这里要注意的最重要的一点是,在if-else-if语句中,只要满足条件,就会执行相应的语句集,忽略其余。如果没有满足条件,则执行else内的语句。

if-else-if的示例

public class IfElseIfExample {

   public static void main(String args[]){
	int num=1234;
	if(num <100 && num>=1) {
	  System.out.println("Its a two digit number");
	}
	else if(num <1000 && num>=100) {
	  System.out.println("Its a three digit number");
	}
	else if(num <10000 && num>=1000) {
	  System.out.println("Its a four digit number");
	}
	else if(num <100000 && num>=10000) {
	  System.out.println("Its a five digit number");			
	}
	else {
	  System.out.println("number is not between 1 & 99999");			
	}
   }
}

输出:

Its a four digit number

查看这些相关的 java 示例

  1. Java 程序:使用if..else..if 查找三个数字中最大的一个
  2. Java 程序:检查数字是正数还是负数
  3. Java 程序:检查数字是偶数还是奇数
posted @ 2024-10-24 18:12  绝不原创的飞龙  阅读(4)  评论(0编辑  收藏  举报