Java(一)数据类型、变量类型、修饰符、运算符

文章目录

  本系列文章:
    Java(一)数据类型、变量类型、修饰符、运算符
    Java(二)分支循环、数组、字符串、方法
    Java(三)面向对象、封装继承多态、重写和重载、枚举
    Java(四)内部类、包装类、异常、日期
    Java(五)反射、克隆、泛型、语法糖、元注解
    Java(六)IO、NIO、四种引用
    Java(七)JDK1.8新特性

一、初识Java

  Java主要分为3个版本:JavaSE(Java平台标准版)、JavaEE(Java平台企业版)和JavaME(Java平台微型版)。

1.1 Java语言的特点

  • 1、跨平台性
      所谓跨平台性,是指java语言编写的程序,经过编译后生成字节码,可以在多个平台的虚拟机上运行。
  • 2、面向对象
      Java是面向对象的语言,使用面向对象的思想,可以降低程序耦合度,提高内聚性。
  • 3、安全性
      安全性可以分为四个层面,即语言级安全性、编译时安全性、运行时安全性、可执行代码安全性。
      语言级安全性指Java的数据结构是完整的对象,这些封装过的数据类型具有安全性。
      编译时要进行Java语言和语义的检查,保证每个变量对应一个相应的值,编译后生成Java类。
      运行时Java类需要类加载器载入,并经由字节码校验器校验之后才可以运行。
  • 4、多线程
       Java语言提供了多线程支持,多线程可以提高程序的运行效率。
  • 5、简单易用,有丰富类库
      简单易用指的是Java程序的开发不必非得依赖特定的开发环境,用notepad等文本编辑工具就可开发。
  • 6、健壮性
      Java语言的强类型机制、异常处理、垃圾的自动收集等。

  强类型语言也称为强类型定义语言,求变量的使用要严格符合定义,所有变量都必须先定义后使用。

1.2 JVM、JRE和JDK的关系

  • JVM
      JVM(Java Virtual Machine):Java虚拟机,是运行Java字节码的虚拟机。JVM有针对不同系统(Windows、Linux、macOS等)的特定实现,目的是使用相同的字节码,都会出现相同的结果。字节码和不同系统的JVM实现是Java语言“一次编译,随处可以运行”的关键。
  • JRE
      JRE(Java Runtime Environment):Java运行环境,包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统默认加载这个包。但是,JRE不能用于创建新程序。
  • JDK
      JDK(Java Development Kit)是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等。JDK可以创建和编译程序。
  • JVM&JRE&JDK关系图
  • JRE和JDK的选择
      如果只是为了运行一下Java程序的话,只需要安装JRE就可以了。如果需要进行一些Java编译的工作,就需要安装JDK了。

1.3 采用字节码的最大好处是什么?

  在Java中,JVM可以理解的代码就叫字节码(后缀名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
  Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植性的特点。所以Java程序运行时比较高效,而且,由于字节码并不针对某一种特定的机器,因此,Java程序无须编译便可在多种不同操作系统的计算机上运行。

1.4 Oracle JDK和Open JDK的对比

  • 1、Oracle JDK大概每6个月发一次主要版本,而Open JDK版本大概每三个月发布一次。
  • 2、Open JDK是一个参考模型并且是完全开源的,而Oracle JDK是Open JDK的一个实现,并且是完全开源的。
  • 3、Oracle JDK比OPen JDK更加稳定。Open JDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。
  • 4、在响应性和JVM性能方面,Oracle JDK与Open JDK相比,提供了更好的性能。
  • 5、Oracle JDK不会为即将发布的版本提供长期支持,用户每次必须通过更新版本来获得最新支持。

1.5 Java和C++的区别

  • 1、都是面向对象的语言,都支持封装、继承和多态。
  • 2、Java不提供指针来直接访问内存,程序内存更加安全。
  • 3、Java的类是单继承的,但是一个类可以实现多个接口,接口可以多继承;C++支持多重继承。
  • 4、Java有自动内存管理机制,不需要程序员手动释放无用内存。

1.6 Java程序的主类

  一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类,主类是Java程序执行的入口点。应用程序的主类一般是public类。示例:

	//主类
	public class TestDemo {
		
		public static void main(String[] args) {
			Test2 test2 = new Test2();
			System.out.print(test2);
		}
	}
	
	class Test2{
	}

1.7 Java语言采用何种编码方案?

  Java语言采用Unicode编码标准,Unicode(标准码),它为每个字符制订了一个唯一的数值,因此在任何的语言,平台,程序都可以放心的使用。Unicode是可以容纳世界上所有文字和字符的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。中文的Unicode码范围是U+4E00到U+9FFF,其中包含了中国所有的基本汉字、部分汉字组合、汉语注音符号、汉字结构描述符等。

1.8 为甚说Java语言“编译与解释并存”?

  高级编程语言按照程序的执行方式分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。比如,要阅读一本英文名著,可以找一个英文翻译人员帮助你阅读。此时有两种选择方式:1、可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读;2、让翻译人员先翻译一段,你在旁边阅读一段,慢慢把书读完。
  Java语言既有编译型语言的特征,也有解释型语言的特征,因为Java程序要经过先编译、后解释两个步骤,由Java编写的程序要先经过编译步骤,生成字节码(.class文件),这种字节码又必须由Java解释器来执行。因此,可以认为Java语言编译与解释并存。

1.9 注释

  注释用于解释说明程序的文字。注释有3种形式:

单行注释 格式: // 注释文字
多行注释 格式: /* 注释文字 */
文档注释 格式:/** 注释文字 */

  在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利 于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。

  • 注意事项
     1、多行和文档注释都不能嵌套使用。
     2、代码的注释不是越详细越好,实际上好的代码本身就是注释。若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。

1.10 传值调用和传引用调用*

  • 形参
      用来接收调用该方法时传递的参数。只有在被调用的时候才分配内存空间(也就是仅仅在方法内有效),一旦调用结束,就释放内存空间。
  • 实参
      传递给被调用方法的值。
  • 传值调用
      传值调用中传递的参数为基本数据类型,参数视为形参。String也是传值调用。
  • 传引用调用
      传引用调用中,如果传递的参数是引用数据类型,参数视为实参。在调用的过程中,将实参的地址传递给了形参,形参上的改变都发生在实参上。
  • 传值调用的例子
      传值调用都是传递的基本类型,示例:
public class BasicTest1 {
	public static void main(String[] args) {
		int num = 1;
		System.out.println("形参初始值:"+num); //形参初始值:1
		print(num);
		System.out.println("形参最终值:"+num); //形参最终值:1
	}
	
	public static void print(int number){
		number++;
		System.out.println("形参修改值:"+number); //形参修改值:2
	}	
}

  在该例子中,main方法里的num是形参,print方法体内的number是实参。
  从这个例子可以看出,虽然方法体print内的number值改变了,但是main方法内的num值仍然是原来的值。这就证明了:传值调用时,方法不会改变实参的值

  • 传引用调用的例子
      传引用调用都是传递的对象类型,示例:
public class BasicTest2 {
	
	private String name;
	public BasicTest2(String name){
		this.name = name;
	}
	public void setName(String name){
		this.name = name;
	}
	public String getName(){
		return this.name;
	}
	
	public static void main(String[] args) {  
		BasicTest2 basicTest2 = new BasicTest2("hello");  
		test(basicTest2);  
		System.out.println(basicTest2.getName());  //hello world
	}  
	
	public static void test(BasicTest2 basicTest2){  
		basicTest2.setName("hello world");
	}  
}

  从这个例子可以看出:传引用调用时,可以改变对象的属性,但属性的引用是的不变的

  • 两种传参方式对比
类别传值调用传引用调用
参数类型基本类型数据对象
是否改变原始值改变的是形参的值,并没有改变实参的值。同时,这个传递是单向的,形参不能传递回实参对对象做何种操作,都不会改变实参对象的引用,但是如果改变了对象的内容,就会改变实参对象的内容
接收值方法其实接收的是参数的一个副本方法接收的是原始值的内存地址,而不是值的副本

  同时,还有一句比较具有迷惑性的话:“在Java里面参数传递都是按值传递”。这句话可以这样理解:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递
  Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

  • 总结
      值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
      引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身 。
      一般认为,Java内的传递都是值传递。

1.11 标识符

  可以由自己命名的地方都称为标识符。例如,对于常量、变量、函数、语句块、类、项目等都需要一个名字,这些我们都统统称为标识符。

  • 标识符的命名规则(硬性要求)
  1. 标识符可以包含英文字母,0-9的数字,$以及_。
  2. 标识符不能以数字开头。
  3. 标识符不是关键字。
  • 标识符的命名规范(非硬性要求)
  1. 类名规范:首字符大写,后面每个单词首字母大写(大驼峰式)。
  2. 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰式)。
  3. 方法名规范:同变量名。

1.12 JDK中常用的包有哪些?

  java.lang:是系统的基础类;
  java.io:输入输出有关的类,比如文件操作等;
  java.nio:提高io包中性能而写的一个新包;
  java.net:与网络有关的类;
  java.util:系统辅助类,特别是集合类;
  java.sql:数据库操作的类。

1.13 Java中的编译期常量是什么?

  公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的public可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。
  这种方式存在的一个问题:当开发者使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当在更新依赖jar文件时,确保重新编译你的程序。
  事实上,不用过分担心此问题,因为在实际开发过程中,编译期常量一般是只新增、不修改的。
  编译期常量一般有2个特点:1、由 public static final 修饰。2、在声明时就被赋值,且赋值语句中只包含常量表达式(即只包含常量、其他编译期常量、基本类型运算符和字符串连接运算符)。

1.14 JDK1.7中的一些新特性

  1)try-with-resource语句:在使用流或者资源的时候,就不需要手动关闭,Java 会自动关闭。
  2)Fork-Join池:某种程度上实现Java版的 Map-reduce。
  3)允许Switch中有String变量和文本。

  switch本质上只支持int类型的条件判断,即使是JDK1.7中的String类型,最终编译的时候还是会被转化为hashCode(int)进行判断。因此,需要注意hashCode重复的问题。
  例如对于字符串“Aa”和“BB”来说,他们的hashCode都是2112,因此在优化是需要注意此类问题,也就是说我们使用hashCode时,必须保证判断添加的值是已知的,并且最好不要出现hashCode重复的问题。

  4)改善异常处理,如允许在同一个 catch 块中捕获多个异常。

1.15 Java程序是如何执行的

  1、先把Java代码编译成字节码,也就是把.java类型的文件编译成.class类型的文件。这个过程的大致执行流程:Java 源代码 -> 词法分析器 -> 语法分析器 -> 语义分析器 -> 字符码生成器 -> 最终生成字节码,其中任何一个节点执行失败就会造成编译失败;
  2、把class文件放置到Java虚拟机,这个虚拟机通常指的是Oracle官方自带的Hotspot JVM;
  3、Java虚拟机使用类加载器(Class Loader)装载class文件;
  4、类加载完成之后,会进行字节码效验,字节码效验通过之后 JVM 解释器会把字节码翻译成机器码交由操作系统执行。但不是所有代码都是解释执行的,JVM 对此做了优化,比如,以 Hotspot 虚拟机来说,它本身提供了 JIT(Just In Time)也就是我们通常所说的动态编译器,它能够在运行时将热点代码编译为机器码,这个时候字节码就变成了编译执行。

  Java程序执行流程图:

二、数据类型

2.1 基础数据类型和引用类型*

  Java中,变量的目的是申请内存来存储值。也就是说,当一个变量被创建时,就需要在内存中申请空间,内存管理系统根据变量的类型为变量分配相应的存储空间,此时分配的空间只能用来存储该类型数据。
  Java有两大数据类型:基本数据类型引用数据类型

  • 1、基本数据类型
      基本数据类型有:整数类型(byte、short、int、long)、浮点类型(float、double)、字符型(char)和布尔型(boolean)。

String不是Java的基本数据类型,默认值为null

  8种基本数据类型的取值范围:

  在实际开发中,每种数据类型的取值范围可以通过其包装类来查看:

类型位数最大值最小值
byteByte.SIZEByte.MAX_VALUEByte.MIN_VALUE
shortShort.SIZEShort.MAX_VALUEShort.MIN_VALUE
intInteger.SIZEInteger.MAX_VALUEInteger.MIN_VALUE
longLong.SIZELong.MAX_VALUELong.MIN_VALUE
floatFloat.SIZEFloat.MAX_VALUEFloat.MIN_VALUE
doubleDouble.SIZEDouble.MAX_VALUEDouble.MIN_VALUE
charCharacter.SIZECharacter.MAX_VALUECharacter.MIN_VALUE

  char类型比较特殊,其最大值和最小值不是数字。Character.MAX_VALUE的值是'\uFFFF',Character.MIN_VALUE的值是'\u0000'

  • 2、引用数据类型
      引用类型有:类(class)、接口(interface)和数组([ ])。
      Java中,引用类型的变量类似于C/C++中的指针,引用类型指向一个对象,指向对象的变量是引用变量。引用变量在声明时被指定为一个特定类型,并且不允许更改。
      对象、数组是引用数据类型,引用类型的默认值是null。
  • 3、两者的区别
     1)概念方面
      基本数据类型:变量名指向具体的数值。
      引用数据类型:变量名不是指向具体的数值,而是指向存数据的内存地址,也就是hash值。
     2)内存方面
      基本数据类型:被创建时,在栈内存中会被划分出一定的内存,并将数值存储在该内存中。
      引用数据类型:被创建时,首先会在栈内存中分配一块空间,然后在堆内存中也会分配一块具体的空间用来存储具体数据,再由栈中引用指向堆中的对象地址。
2.1.1 整型数据

  整型数据有4种:byte、short、int和long。

  • byte
      8位(1字节),有符号整数,范围为-27 ~ 27-1,默认值是0。
  • short
      16位(2字节),有符号整数,范围为-215 ~ 215-1,默认值是0。
  • int
      32位(4字节),有符号整数,范围为-231~ 231-1(约-21亿 ~ 21亿),默认值为0,一般整型变量默认为int型
  • long
      64位(8字节),有符号整数,范围为-263 ~ 263-1,默认值为0L。赋予long型变量初始值时,需要在数字后面加上L
  • 注意事项:数值溢出
      在进行整型数据运算时,要注意溢出的问题,比如以int型为例,Integer.MAX_VALUE+1的运算结果是Integer.MIN_VALUE。即:
	int i1 = Integer.MAX_VALUE;
	int i2 = Integer.MIN_VALUE;
	System.out.println("i1+1:"+(i1+1));  //-2147483648
	System.out.println("i2:"+i2);   //-2147483648
2.1.2 浮点型数据

  浮点型数据有2种:float和double。

  • float
      单精度,32位(4字节),默认值是0.0f。
  • double
      双精度,64位(8字节),默认值是0.0d。
      浮点型变量默认为double型
2.1.3 布尔型数据

  boolean,只有两个取值:true和false,默认值是false。

2.1.4 字符型数据

  char,16位Unicode字符,范围为0 ~ 215-1(\u0000到\uffff),可存储任意字符,默认值是’u0000’。
  一个char类型变量中可以存储一个中文汉字,因为char类型变量使用的默认编码格式是 Unicode,一个char类型占2个字节(16byte),所以放一个中文是没问题的。示例:

	cahr var = '张';

2.2 非基本数据类型之BigDecimal*

2.2.1 浮点型数据的运算会有误差

  看一个例子:

	System.out.println(0.06+0.01);
	System.out.println(2.0-1.1);
	System.out.println(3.3*3);
	System.out.println(3.3/3);

  在我们的预想中,这4个语句的执行结果应该是0.07、0.9、9.9、1.1,但实际的结果:

0.06999999999999999
0.8999999999999999
9.899999999999999
1.0999999999999999

  可以看出:浮点数的计算是可能不准确的,float和double类型的使用局限:

  • 1、单精度浮点型变量float可以处理6 ~ 7位有效数,双精度浮点型变量double可以处理15~16位有效数,在实际应用中,如果需要对更大或者更小的数进行运算和处理,这时候float和double就无能为力了。
  • 2、float和double类型的主要设计目标是为了科学计算和工程计算,他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合

  商业计算往往要求结果精确,这时候就需要用到BigDecimal。BigDecimal的使用示例:

public class DoubleTest {
	public static void main(String[] args)  {
		System.out.println(add(0.06,0.01));     //0.07
		System.out.println(subtract(2.0,1.1));  //0.9
		System.out.println(multiply(3.3,3));    //9.9
		System.out.println(divide(3.3,3,1));    //1.1
	}
	/* 加法运算*/
	public static double add(double x, double y) {
		BigDecimal vala = new BigDecimal(Double.toString(x));
		BigDecimal valb = new BigDecimal(Double.toString(y));
		return vala.add(valb).doubleValue();
	}
	
	/*减法运算*/
	public static double subtract(double x, double y) {
		BigDecimal vala = new BigDecimal(Double.toString(x));
		BigDecimal valb = new BigDecimal(Double.toString(y));
		return vala.subtract(valb).doubleValue();
	}
	
	/*乘法运算*/
	public static double multiply(double x, double y) {
		BigDecimal vala = new BigDecimal(Double.toString(x));
		BigDecimal valb = new BigDecimal(Double.toString(y));
		return vala.multiply(valb).doubleValue();
	}
	
	/*除法运算(结果进行四舍五入)*/
	public static double divide(double x, double y, int scale) throws ArithmeticException {
		if (scale < 0) {
			throw new ArithmeticException("精确度不能小于0");
		}
		
		BigDecimal vala = new BigDecimal(Double.toString(x));
		BigDecimal valb = new BigDecimal(Double.toString(y));
		return vala.divide(valb, scale, BigDecimal.ROUND_HALF_EVEN).doubleValue();
	}
}
2.2.2 使用BigDecimal的注意事项

  BigDecimal的计算原则有2个。

  • 第一原则:使用BigDecimal表示和计算浮点数,且使用字符串的构造方法来初始化 BigDecimal
      示例:
	BigDecimal a = new BigDecimal(2.3);
	BigDecimal b = new BigDecimal("2.3");
	//2.29999999999999982236431605997495353221893310546875
	System.out.println(a);
	//2.3
	System.out.println(b);

  从这个例子能看出,使用参数类型为double的构造方法时,结果和预期的不同,有一定的不可预知性。使用String类型的构造方法时,结果可预期。因此,建议使用String方式的构造方法。

  • 第二原则:浮点数的字符串格式化也要通过BigDecimal进行
      示例:
    BigDecimal num1 = new BigDecimal("3.35");
    //向下舍入
    BigDecimal num2 = num1.setScale(1, BigDecimal.ROUND_DOWN);
    System.out.println(num2);  //3.3
    //四舍五入方式
    BigDecimal num3 = num1.setScale(1, BigDecimal.ROUND_HALF_UP);
    System.out.println(num3);  //3.4
  • BigDecimal的比较
      对于BigDecimal对象而言,equals方法比较的是value和scale。按常规数值进行计算,可能会出现问题,示例:
    //false
    System.out.println(new BigDecimal("1.0").equals(new BigDecimal("1")));

  BigDecimal的equals和hashCode方法会同时考虑value(整数部分)和scale(小数部分),如果结合HashSet或HashMap使用的话就可能会出现麻烦。比如,把值为1.0的BigDecimal加入 HashSet,然后判断其是否存在值为1的BigDecimal,得到的结果是 false:

    Set<BigDecimal> hashSet1 = new HashSet<>();
    hashSet1.add(new BigDecimal("1.0"));
    //false
    System.out.println(hashSet1.contains(new BigDecimal("1")));

  解决这个问题的办法有两个:

  1. 使用TreeSet替换 HashSet
      TreeSet不使用hashCode方法,也不使用equals比较元素,而是使用compareTo方法,所以不会有问题。示例:
    Set<BigDecimal> treeSet = new TreeSet<>();
    treeSet.add(new BigDecimal("1.0"));
    //true
    System.out.println(treeSet.contains(new BigDecimal("1")));
  1. 去掉尾部的零(常用)
      把BigDecimal存入HashSet或HashMap前,先使用stripTrailingZeros方法去掉尾部的零,比较的时候也去掉尾部的0,确保value相同的BigDecimal,scale也是一致的。示例:
    Set<BigDecimal> hashSet2 = new HashSet<>();
    hashSet2.add(new BigDecimal("1.0").stripTrailingZeros());
    //true
    System.out.println(hashSet2.contains(new BigDecimal("1.000").stripTrailingZeros()));
  • toString和toPlainString
      toString会使用科学计数法来表示,而toPlainString不会。示例:
	BigDecimal bg = new BigDecimal("1E11");
    System.out.println(bg.toPlainString());
    System.out.println(bg.toString());

  结果:

100000000000
1E+11

2.3 数据类型转换*

  Java中,整型、常量、字符型数据可以混合运算。不同类型的数据进行运算时,会先转换为同一类型(更高的数据类型),再进行运算。
  各变量优先级如下:
    低 ----------------------------------------------------> 高
    byte,short,char—> int —> long—> float —> double
  图示:

  在8种基本数据类型中,char是比较特殊的一种类型,如果char类型转成byte,short类型的时候,需要强转。示例:

	char num1 = 1;
	int num2 = num1;
	double num3 = num1;
	short num4 = num1; //编译报错
	byte num5 = num1;  //编译报错	

  数据类型转换时需要注意的规则:

  1. 不能对boolean类型进行类型转换。
  2. 把优先级高的转换为优先级低时必须使用强制类型转换。
  3. 转换过程中可能导致溢出或损失精度。

  自动类型转换发生在转换前的数据类型的位数低于转换后的数据类型。
  强制类型转换发生的条件是转换前后的数据类型必须是兼容的。
  强制类型转换的格式为:(type)value。
  类型转换可分为两种:隐式转换和显式转换。

2.3.1 隐式转换

   从表示范围小的类型转换为表示范围大的类型,可以直接转换,称为隐式转换。示例:

		short m = 912;
        int n = m;

   因为int可以存储的范围要比short存储的范围大,所以short类型可以直接转换成int类型。

2.3.2 隐式转换所产生的问题

  在使用+=、-=等符号时,常常伴随着隐式类型转换的进行。如果参与运算的两个变量数据类型相同,一般不会有什么问题。如果两个变量数据类型不同,就需要注意了。

  • 1、类型小于int的整型变量和int类型变量相加减
        byte a=1;
        a=a+4;
        System.out.println(a);

  在执行语句’a=a+4’时,a是byte类型,而数字4默认是int类型。在java中,执行byte变量(非int、非long整形变量)+int变量时,会进行自动类型转换成int类型 ,所以a+4会转换成int类型。而变量a还是byte类型,将int 类型的a+4赋值给byte类型的a ,无疑会报错。
  解决方法1:可以使用强制类型转换,示例:

        byte a=1;
        //强制将运算的结果转换成byte类型,所以没问题
        a=(byte)(a+4);
        System.out.println(a);

  解决方法1:使用+=,示例:

        byte a=1;
        //+=运算符会隐式地将加操作的结果类型,强制转换为持有结果的类型,所以也没问题
        a+=1;
        System.out.println(a);
  • 2、两个类型小于int的整型变量相加减
		byte a = 127;
		byte b = 0;
		b = a + b; 

  粗看之下好像也没问题,其实也有问题,报错如下:

  此时建议将上述代码,修改为如下形式:

		byte a = 127;
		byte b = 0;
		a += b; 

  上面现象产生的原因是:整型数据和整型数据进行运算的时候,如果两边的值都是小于或者等于int的,那么其结果就是int型

  • 3、至少有一个类型大于int类型的整型变量相加减
		int a = 0;
		long b = 10L;

  a + b 的结果是long型,原因是:整型数据和整型数据进行运算的时候,如果有一边的长度超过int,那么运算结果就按照最长的长度

  • 4、隐式转换相关的建议
      当操作的变量涉及非int型变量时,建议使用'a += b',不用'b = a + b'。这样无论非int整形变量的类型是大于int还是小于int,运算都不会直接报错。
2.3.3 显式转换

   从表示范围大的类型转换为表示范围小的类型,需要强制转换,称为显式转换。示例:

		double m = 9.12;
        int n = (int)m;
        System.out.println(n); //9

  从该例子可以看出,显式转换可能伴随着精度的丢失

2.4 数据类型的相关问题

2.4.1 short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

  对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int型,需要强制转换类型才能赋值给 short 型。这种现象产生的原因是:整型数据和整型数据进行运算的时候,如果两边的值都是小于或者等于int的,那么其结果就是int型

  而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

2.4.2 a=a+b与a+=b有什么区别吗?

  += 操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。示例:

	byte a = 127;
	byte b = 127;
	b = a + b; // 报编译错误:cannot convert from int to byte
	b += a;

  以下代码是否有错,有的话怎么改?

	short s1= 1;
	s1 = s1 + 1;

  有错误。short类型在进行运算时会自动提升为int类型,也就是说s1+1的运算结果是int类型,而s1是short类型,此时编译器会报错。
  正确写法:

	short s1= 1;
	s1 += 1;

  +=操作符会对右边的表达式结果强转匹配左边的数据类型,所以没错。

2.4.3 数值溢出问题

  不管是int还是long,所有的基本数值类型都有超出表达范围的可能性。比如,对Long的最大值进行+1操作。示例:

    long l = Long.MAX_VALUE;
    //-9223372036854775808
    System.out.println(l + 1);
    //true
    System.out.println(l + 1 == Long.MIN_VALUE);

  改进方式有下面 2 种。

  • 1、使用Math类的addExact、subtractExact等xxExact方法进行数值运算
      这些方法可以在数值溢出时主动抛出异常。示例:
	try {
	    long l = Long.MAX_VALUE;
	    System.out.println(Math.addExact(l, 1));
	} catch (Exception ex) {
	    ex.printStackTrace();
	}

  以上代码运行时,会抛出异常:

java.lang.ArithmeticException: long overflow
	at java.lang.Math.addExact(Math.java:809)
	at com.test.TestDemo.main(TestDemo.java:21)
  • 2、使用大数类BigInteger
      BigDecimal是处理浮点数的专家,而BigInteger则是对大数进行科学计算的专家。示例:
    BigInteger i = new BigInteger(String.valueOf(Long.MAX_VALUE));
    System.out.println(i.add(BigInteger.ONE).toString());

    try {
        long l = i.add(BigInteger.ONE).longValueExact();
    } catch (Exception ex) {
       ex.printStackTrace();
    	}

  结果:

9223372036854775808
java.lang.ArithmeticException: BigInteger out of long range
	at java.math.BigInteger.longValueExact(BigInteger.java:4531)
	at com.test.TestDemo.main(TestDemo.java:24)

  可以看出:通过BigInteger对Long的最大值加1是没问题的。当尝试把结果转换为Long类型时,会提示BigInteger out of long range异常。

2.4.4 char类型能不能转成int类型?能不能转化成string类型,能不能转成double类型?

  char在java中也是比较特殊的类型,它的int值从1开始,一共有2的16次方个数据。
  char<int<long<float<double;Char类型可以隐式转成int,double类型,但是不能隐式转换成string;如果char类型转成byte,short类型的时候,需要强转。

2.4.5 switch是否能作用在byte上?是否能作用在long上?是否能作用在String上?

  在JDK1.5以前,switch(expr)中,expr只能是byte、short、char、int;
  从 JDK1.5开始,Java 中引入了枚举类型,expr也可以是enum类型;
  从JDK1.7开始,expr 还可以是字符串(String);
  长整型(long)在目前所有的版本中都是不可以的。

2.4.6 float f=3.4;是否正确

  不正确。3.4是(double)双精度数,将双精度型(double)赋值给浮点型(float)属于下转型,会造成精度损失。因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;。

2.4.7 字符型常量和字符串常量的区别?
字符型常量字符串常量
形式单引号括起来的一个字符双引号括起来的若干个字符
含义相当于一个整型值(ASCII值),ke一参加表达式运算代表一个地址值(该字符串在内存中存放的位置)
占用内存空间2个字节若干个字节
2.4.8 String转int型,判断能不能转?如何转?

  可以转,用Integer.parseInt(str)。同时需要处理异常,主要为 NumberFormatException,会出现这个异常的常见情况:

1)当输入内容不是数字时,如"abcd";
2)当你输入内容为空时。
3)当你输入超出Integer.MAX_VALUE时,此时可以用Long.parseLong(str)转换为long型数据。

2.4.9 char型变量中能不能存储一个中文汉字,为什么?

  可以。Java默认Unicode编码,Unicode码占16位,char两个字节刚好16位。

2.4.10 怎么将byte转换为String?

  在Java中将byte[]类型转换为String类型,可以使用String的构造函数或者静态方法。

  • 1、使用String构造函数
      示例:
	byte[] byteArray = {97, 98, 99}; 
	String str = new String(byteArray);
  • 2、使用静态方法valueOf()
      示例:
	byte[] byteArray = {97, 98, 99}; 
	String str = String.valueOf(byteArray);

  需要注意的是,在转换过程中需要指定字符集,否则会使用平台默认字符集进行转换。比如byte数组中包含的是UTF-8编码的字符串,则应该使用 “UTF-8” 指定字符集。示例:

	byte[] byteArray = {97, 98, 99}; 
	String str = new String(byteArray, "UTF-8");

	byte[] byteArray = {97, 98, 99}; 
	String str = String.valueOf(byteArray, "UTF-8");
2.4.11 能将int强制转换为byte类型的变量吗?如果该值大于byte类型的范围,将会出现什么现象?

  能转。Java中int是32位的,而byte是8位(数值范围是从-128到127)的。所以,如果强转时,int类型的高24位将会被丢弃。

2.4.12 3*0.1 == 0.3将会返回什么?

  false。因为3*0.1的结果是double类型,和0.3并不相等。示例:

	System.out.println(3*0.1);  //0.30000000000000004
	System.out.println(0.3);   //0.3
    System.out.println(3*0.1 == 0.3); //false
2.4.13 32位和64位的JVM,int类型变量的长度是多数?

  32位和64位的JVM中,int类型变量的长度是相同的,都是32位(4个字节)。

三、变量类型

  Java中的变量类型大致可分为3种:

  • 1、局部变量
      方法体中的变量。
  • 2、实例变量
      方法体之外的变量,无static修饰。
  • 3、静态变量
      方法体之外的变量,用static修饰。

3.1 局部变量

  局部变量的特征:

  1. 声明在方法、构造方法或者语句块中。
  2. 在方法、构造方法、或者语句块被执行的时候创建,执行完后,变量将会被销毁。
  3. 只在声明它的方法、构造方法或者语句块中可见,访问修饰符不能用于局部变量
  4. 所占用的内存空间在栈。
  5. 没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。

  局部变量示例:

	public int addOne(int a) {
		int ONE = 1;
		return a + ONE;
	}

  假如用访问修饰符修饰,局部变量,会报错:

  假如局部变量没有赋予默认值,也会报错:

3.2 实例变量

  实例变量的特征:

  1. 声明在一个类中,但在方法、构造方法和语句块之外。
  2. 当一个对象被实例化之后,每个实例变量的值就跟着确定。
  3. 实例变量在对象创建的时候创建,在对象被销毁的时候销毁。
  4. 访问修饰符可以修饰实例变量。
  5. 实例变量对于类中的方法、构造方法或者语句块是可见的。
  6. 实例变量具有默认值,数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null
  7. 实例变量可以直接通过变量名访问。

  实例变量示例:

public class TestDemo {
	
	private int a;
	int ONE = 1;
	
	public TestDemo() {
		a = 2;
	}
	
	public static void main(String[] args) {
		TestDemo testDemo = new TestDemo();
		//a:2,addOne(5):6
		System.out.println("a:"+testDemo.a+",addOne(5):"+testDemo.addOne(5)); 
	}
	
	public int addOne(int num) {
		return num + ONE;
	}
}

3.3 静态变量

  静态变量也称类变量,其特征:

  1. 在方法体之外,用static关键字声明。
  2. 静态变量的数量与该类创建的对象数量无关,只有一个。
  3. 静态变量在第一次被访问时创建,在程序结束时销毁
  4. 默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。
  5. 可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。

  静态变量被声明为public static final类型时,静态变量名称一般建议使用大写字母,此时的作用就是与常量相同。
  静态变量示例:

public class TestDemo {
	
	private static int a;
	public static final int ONE = 1;
	
	public static void main(String[] args) {
		//a:0,addOne(5):6
		System.out.println("a:"+a+",addOne(5):"+addOne(5)); 
	}
	
	public static int addOne(int num) {
		return num + ONE;
	}
}

3.4 三种变量的比较*

  • 1、成员变量和局部变量
成员变量局部变量
属于谁方法
能被什么修饰符修饰public,private,static,finalfinal
存储位置
生命周期是对象的一部分,随着对象的创建而存在随着方法的调用而自动消失
是否有默认值

  在使用变量时需要遵循的原则为就近原则:首先在局部范围找,有就使用;接着在成员位置找。

  • 2、静态变量和实例(成员)变量
静态变量实例变量
属于谁对象
在内存中有几份1N(创建几次对象,就有几份)
加载次数在类的加载过程中,JVM只为静态变量分配1次内存空间每次创建对象,都会为每个对象分配实例变量内存空间
调用方式一般通过类名.静态变量方式调用通过对象名.实例名调用

四、修饰符

  Java中修饰符主要分为:访问修饰符和非访问修饰符。

4.1 访问修饰符*

  顾名思义,该类修饰符重要控制访问权限。不同访问修饰符的修饰对象及权限,如下:

  1. default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  2. private : 在同一类内可见。使用对象:变量、方法。
  3. protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。
  4. public : 对所有类可见。使用对象:类、接口、变量、方法。

  4种修饰符的访问权限:

修饰符当前类同包类子类其他类
publicYYYY
protectedYYYN
defaultYYNN
privateYNNN

  接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public

  • private特征
      声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。也就是说,声明为private的方法和变量,都是当前类内部使用的,外部不能访问。

  • protected特征
     1)子类与基类在同一包中,被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问。
     2)子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。

  • 访问控制和继承规则
      1)父类中声明为public的方法,在子类中也必须为public。
      2)父类中声明为protected的方法,在子类中要么声明为protected,要么声明为public,不能声明为private。
      3)父类中声明为private的方法,不能够被继承。
      这三条规则体现的是访问权限放大机制,即子类方法的方法访问权限>=父类的方法访问权限

  关于private、default、protected、public四种修饰符在平时开发中的使用,可以简单参考以下几条原则:

  1. 属性通常使用private封装起来。
  2. 方法一般使用public用于被调用。
  3. 会被子类继承的方法,通常使用protected。
  4. 作用范围最小原则。即能用private就用private,不行就放大一级,用default,再不行就用protected,最后用public, 这样就能把数据尽量的封装起来

4.2 非访问修饰符

 非访问修饰符,主要有static、final、abstract。此处先简单介绍,后面会详细说明。

  • 1、static
      static修饰变量,就是静态变量。
      static修饰方法,就是静态方法,通过类名而不是该类实例化的对象调用。
  • 2、final
      final修饰变量,声明的时候必须初始化,不能被修改。
      final修饰方法,可以被子类继承,但是不能被子类重写。
      final修饰类,不能被继承。
  • 3、abstract
      abstract修饰类,不能被实例化。
      abstract修饰方法,一般需要子类重写抽象方法,除非子类也是抽象类。

五、运算符

  Java表达式是变量、常量、运算符、方法调用的序列,执行指定的计算并返回特定的值。
  表达式一般是按运算符来划分的,表达式种类:

1、算术表达式
2、关系表达式
3、布尔逻辑表达式
4、位运算表达式
5、赋值表达式
6、条件表达式

5.1 算术运算符

  假设整数变量A的值为10,变量B的值为20。

运算符描述例子
+加法:相加运算符两侧的值A + B 等于 30
-减法:左操作数减去右操作数A – B 等于 -10
*乘法:相乘操作符两侧的值A * B等于200
/除法:左操作数除以右操作数B / A等于2
%取余:左操作数除以右操作数的余数B%A等于0
++自增:操作数的值增加1B++ 或 ++B 等于 21
--自减:操作数的值减少1B-- 或 --B 等于 19

  这几个算术运算符中,比较特殊的是后两个:

前缀自增自减法(++a): 先进行自增或者自减运算,再进行表达式运算。
后缀自增自减法(a++): 先进行表达式运算,再进行自增或者自减运算。

  示例:

    int a = 3; 
    int b = ++a; 
    int c = 3;
    int d = --c; 
    //进行自增运算后的值等于4
    System.out.println("进行自增运算后的值等于"+b);
    //进行自减运算后的值等于2
    System.out.println("进行自减运算后的值等于"+d);

5.2 关系运算符

  假设整数变量A的值为10,变量B的值为20。

运算符描述例子
==检查如果两个操作数的值是否相等,如果相等则条件为真(A == B)为假
!=检查如果两个操作数的值是否相等,如果值不相等则条件为真(A != B) 为真
>检查左操作数的值是否大于右操作数的值,如果是那么条件为真(A> B)为假
<检查左操作数的值是否小于右操作数的值,如果是那么条件为真(A <B)为真
>=检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真(A> = B)为假
<=检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真(A <= B)为真

5.3 位运算符

  位运算符应用于char、short、int、long和byte型数据。
  假设整数变量A的值为60和变量B的值为13。

运算符描述例子
&如果相对应位都是1,则结果为1,否则为0(A&B),得到12,即0000 1100
^如果相对应位值相同,则结果为0,否则为1(A ^ B)得到49,即 0011 0001
~按位取反运算符翻转操作数的每一位,即0变成1,1变成0(〜A)得到-61,即1100 0011
<<按位左移运算符。左操作数按位左移右操作数指定的位数A << 2得到240,即 1111 0000
>>按位右移运算符。左操作数按位右移右操作数指定的位数A >> 2得到15即 1111
>>>按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充移动得到的空位以零填充。 A>>>2得到15即0000 1111

5.4 逻辑运算符

  假设布尔变量A为真,变量B为假。

运算符描述例子
&&称为逻辑与运算符。当且仅当两个操作数都为真,结果才为真(A && B)为假
!称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false!(A && B)为真

  ||称为逻辑或操作符。如果任何两个操作数任何一个为真,结果为真。
  ||为短路或运算符,即如果左边的运算结果已经是true,右边的语句不会再执行。
  &&为短路逻辑与运算符,即前面的判断条件为false时,不再进行后面条件的判断。
  关于短路功能,测试代码:

	int i=1;
	if(i==2 && (i++)==2)
		System.out.println("test");
	System.out.println("i:"+i);  //i:1

  由这个例子可以看出:当&&前面的判断条件i==2不成立时,后面的判断语句也没有进行

5.5 赋值运算符

运算符描述例子
=简单的赋值运算符,将右操作数的值赋给左侧操作数C = A + B将把A + B得到的值赋给C
+=加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数C + = A等价于C = C + A
-=减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数C - = A等价于C = C - A
*=乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数C * = A等价于C = C * A
/=除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数C / = A,C 与 A 同类型时等价于 C = C / A
%=取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数C%= A等价于C = C%A
<<=左移位赋值运算符C << = 2等价于C = C << 2
>>=右移位赋值运算符C >> = 2等价于C = C >> 2
&=按位与赋值运算符C&= 2等价于C = C&2
^=按位异或赋值操作符C ^ = 2等价于C = C ^ 2

  特殊赋值运算符(如:+=)可以在不改变原有数据类型的情况下实现运算,如果两个值数据类型不一致,会进行强制数据类型转换。测试代码:

	int i=1;
	i+=2;
	System.out.println("i:"+i);  //i:3
	i+=2.0f;
	System.out.println("i:"+i);  //i:5

5.6 条件运算符

  即三元运算符,形式为:

	x = (expression) ? trueVal : falseVal

  示例:

	int gradeA=98;
	int gradeB=93;
	int betterGrade=0;
	betterGrade=gradeA>gradeB?gradeA:gradeB;
	System.out.println("两个同学中较好的成绩是:"+betterGrade); //两个同学中较好的成绩是:98

5.7 instanceof运算符*

  instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:

	boolean result = obj instanceof Class

  其中obj 为一个对象,Class 表示一个类或者一个接口,当 obj为Class的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
  此处需要注意的是:编译器会检查obj是否能转换成右边的class类型,如果不能转换则直接报错。如果不能确定类型,则通过编译,具体看运行时定。

5.7.1 obj必须为引用类型,不能是基本类型

  instanceof运算符只能用作对象的判断(以下例子中编译不通过,因为i必须是引用类型,不能是基本类型),所以下面两种写法都会编译报错:

5.7.2 obj为null时的情况

  Java中,数据分为基本数据类型和引用数据类型,null不属于这两种类型,自然不能算作引用类型。因此,对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回 false。示例

	System.out.println(null instanceof Integer); //false
5.7.3 obj为类的实例对象的情况

  这是最常用的情况,示例:

	Integer integer = new Integer(1);
	System.out.println(integer instanceof  Integer); //true
5.7.4 obj为class接口的实现的情况

  此处以List接口和其数组实现ArrayList为例,instanceof运算符比较的是instanceof符号前面的引用的对象是否后面接口的实现,示例:

	ArrayList arrayList = new ArrayList();
	System.out.println(arrayList instanceof List);//true
	List list = new ArrayList();
	System.out.println(list instanceof ArrayList);//true
5.7.5 obj为class类的直接或间接子类的情况

  该情况与上种情况类似,instanceof运算符比较的是instanceof符号前面的引用的对象是否后面类或其子类,示例:

/*父类*/
public class Person {

}
/*子类*/
public class Man extends Person{

}
/*测试类*/
public class ClassTest {
	public static void main(String[] args)  {
		Person p1 = new Person();
		Person p2 = new Man();
		Man m1 = new Man();
		System.out.println(p1 instanceof Man);//false
		System.out.println(p2 instanceof Man);//true
		System.out.println(m1 instanceof Man);//true
	}
}

  在前面提过“编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定”,这句话可以用上面的Person类和Man类来说明。示例:

	System.out.println(p1 instanceof String);//编译报错
	System.out.println(p1 instanceof List);//运行时报错
	System.out.println(p1 instanceof List<?>);//运行时报错
	System.out.println(p1 instanceof List<Person>);//编译报错
5.7.6 instanceof运算符的实现

  先看JDK1.8中的实现,用伪代码表示为:

	boolean result;
	if (obj == null) {
	    result = false;
	} else {
	    try {
	        T temp = (T) obj; // checkcast
	        result = true;
	    } catch (ClassCastException e) {
	        result = false;
	    }
	}

  在执行obj instanceof T运算时,首先,会检查obj的类型是否是引用类型或空类型,如果不是则会发生编译时错误;
  其次,如果obj强制转换为T时发生编译错误,则关系表达式的instanceof同样会产生编译时错误;
  最后,在运行时,如果T的值不为null,并且obj可以转换为T而不引发ClassCastException,则instanceof运算符的结果为true。 否则结果是false。
  简单来说就是:如果obj不为null,并且(T)obj不抛ClassCastException异常则该表达式值为true ,否则值为false

5.8 运算符优先级

  Java运算符优先级(从上往下依次递减):

类别操作符运算顺序
后缀() [] . (点操作符)左到右
一元+ + – (此处指的是先执行自增或自减,再执行表达式)左到右
一元+ + – (此处指的是先执行表达式,再执行自增或自减) ! 〜右到左
乘性* /%左到右
加性+ -左到右
移位>> >>> <<左到右
关系> > = < < =左到右
相等== !=左到右
按位与、异或、或&、^、
逻辑与、或&&、
条件?:左到右
赋值= + = - = * = / =%= >> = << =&= ^ ==

  如果在程序中,要改变运算顺序,可以使用()。

5.9 判等问题

  比较值的内容,除了基本类型只能使用 == 外,其他类型都需要使用 equals。
  对于自定义的类型,如果要实现Comparable,请记得equals、hashCode、compareTo三者逻辑一致。

5.10 运算符相关问题

5.10.1 &和&&的区别
  • &
      按位与;逻辑与。
      按位与:0&1=0;0&0=0;1&1=1。
      逻辑与:a == b & b == c(即使a==b已经是false,程序还会继续判断b是否等于c)。
  • &&
      短路与。
      a == b & b == c(当a==b已经是false,程序则不会继续判断b是否等于c)。
5.10.2 为什么Java不支持运算符重载
  • 1、简单性和清晰性
      清晰性是Java设计者的目标之一。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器, 或减慢JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会, 以保证Java中运算符的行为。
  • 2、避免编程错误
      因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。由于Java和JVM已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会, 成为编程错误之源, 因此没有多大意义。
  • 3、JVM复杂性
      从JVM的角度来看,支持运算符重载使问题变得更加困难。与相对简单的JVM相比,复杂的JVM可能导致JVM更慢,并为保证在Java中运算符行为的确定性从而减少了优化代码的机会。
  • 7
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是关于Java数据类型运算符的一些基本知识: Java数据类型可分为两类:基本数据类型和引用数据类型。基本数据类型包括整数类型、浮点数类型、字符类型和布尔类型。引用数据类型包括类、接口、数组等。 Java运算符包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、三目运算符等。 下面是一些常见的Java数据类型运算符的示例: 整数类型: ```java int a = 10; // 声明一个整数类型变量 long b = 100L; // 声明一个长整数类型变量 ``` 浮点数类型: ```java float c = 1.23f; // 声明一个单精度浮点数类型变量 double d = 4.56; // 声明一个双精度浮点数类型变量 ``` 字符类型: ```java char e = 'a'; // 声明一个字符类型变量 ``` 布尔类型: ```java boolean f = true; // 声明一个布尔类型变量 ``` 算术运算符: ```java int g = 10; int h = 5; int i = g + h; // 加法运算 int j = g - h; // 减法运算 int k = g * h; // 乘法运算 int l = g / h; // 除法运算 int m = g % h; // 取模运算(求余数) ``` 关系运算符: ```java int n = 10; int o = 5; boolean p = n > o; // 大于运算 boolean q = n < o; // 小于运算 boolean r = n >= o; // 大于等于运算 boolean s = n <= o; // 小于等于运算 boolean t = n == o; // 等于运算 boolean u = n != o; // 不等于运算 ``` 逻辑运算符: ```java boolean v = true; boolean w = false; boolean x = v && w; // 逻辑与运算 boolean y = v || w; // 逻辑或运算 boolean z = !v; // 逻辑非运算 ``` 位运算符: ```java int aa = 10; int bb = 5; int cc = aa & bb; // 按位与运算 int dd = aa | bb; // 按位或运算 int ee = aa ^ bb; // 按位异或运算 int ff = ~aa; // 按位取反运算 int gg = aa << 2; // 左移运算 int hh = aa >> 2; // 右移运算 ``` 赋值运算符: ```java int ii = 10; ii += 5; // 等价于 ii = ii + 5 ii -= 5; // 等价于 ii = ii - 5 ii *= 5; // 等价于 ii = ii * 5 ii /= 5; // 等价于 ii = ii / 5 ii %= 5; // 等价于 ii = ii % 5 ``` 三目运算符: ```java int jj = 10; int kk = 5; int ll = jj > kk ? jj : kk; // 如果 jj > kk,则返回 jj,否则返回 kk ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值