抽象、封装、继承、多态为Java面向对象的最重要的四个特征(其它特征还有诸如多线程、API多、安全性高、跨平台等等)
(1)抽象:Java是面向对象的解释性编程语言,面向对象讲究的万物皆对象,是将一类对象的共同特征总结出来构造类的过程,包括数据抽象(属性)和行为抽象(方法/函数),而不会关注行为的细节是什么样子。
(2)封装:将对象中的数据封闭包装起来,只能通过方法/函数操作数据,使用者只能使用对象的属性和方法/函数,而不知道对象的具体实现。
打个比喻就相当于一台自动贩卖机,这个贩卖机相当于对象,贩卖机中有各种饮料,贩卖机有贩售饮料的方法,你只需要投币,就能拿到等值的饮料,但是你不知道你投币以后你的饮料是怎样从贩卖机中出来的。(说到封装就不得不说Java中的访问限制private、default、protect、public),一个简单常见的例子就是set、get方法访问类中私有属性)。
(3)继承:继承性:继承是有联系的类所构成的层次模型。它可以重用类,并且提供了明确表达共性的方式,即原始类的派生类(子类)可以从基类(父类)那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。这样使设计新类的过程大大简化。Object是所有类的超类。
(4)多态:多态性:多态是在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。多态性包括参数多态性和包含多态性。Java编辑器会自动根据所传递的参数进行判断,根据运行时对象的类型不同而执行不同的操作,这样很好地解决了应用程序函数同名问题。多态性丰富了对象的内容,扩大了对象的适应性,改变了对象单一继承的关系。(多态就应该对重载和重写的概念很清楚,重载与重新是实现多态的重要机制)
(1)继承:对象的一个新类可以从现有的类中派生,派生类可以从它的基类那继承方法和实例变量,且派生类可以修改或新增新的方法使之更适合特殊的需求。
(2)封装:将客观事物抽象成类,每个类可以把自身数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。
(3)多态:允许不同类的对象对同一消息作出响应。不同对象调用相同方法即使参数也相同,最终表现行为是不一样的。
Java提供了两种用于多态的机制,分别是重载(运行时多态)与覆盖(编译时多态)。
(1)重载:重载是指同一个类中有多个同名的方法,但这些方法有不同的参数,在编译期间就可以确定调用哪个方法。
(2)覆盖:覆盖是指派生类重写基类的方法,使用基类指向其子类的实例对象,或接口的引用变量指向其实现类的实例对象,在程序调用的运行期根据引用变量所指的具体实例对象调用正在运行的那个对象的方法,即需要到运行期才能确定调用哪个方法。
Java实现跨平台的原理是通过Java虚拟机来实现的,Java源文件为后缀.java的文件,经过编译器编译为后缀.class文件。这些.class文件可以在JVM(Java虚拟机)中运行,而JVM底层屏蔽了不同操作系统的差异,这样就使得能够“一次编译,随处运行”。
字节序是指多字节数据在计算机内存中存储或网络传输时个字节的存储顺序。通常由小端和大端两组方式。
(1)小端:低位字节存放在内存的低地址端,高位字节存放在内存的高地址。
(2)大端:高位字节存放在内存的低地址端,低位字节存放在内存的高地址端。
Java语言的字节序是大端。
(1)JDK:Java开发工具包(Java Development Kit),提供了Java的开发环境和运行环境。
(2)JRE:Java运行环境(Java Runtime Environment),提供了Java运行所需的环境。
JDK包含了JRE。如果只运行Java程序,安装JRE即可。要编写Java程序需安装JDK.
java中对象可以分为实例对象和Class对象,每一个类都有一个Class对象,其包含了与该类有关的信息。
获取Class对象的方法:
(1)Class.forName(“类的全限定名”),例如加载mysql的JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
(2)实例对象.getClass(),例如创建日志对象
private final Logger log = LoggerFactory.getLogger(getClass());
(3)类名.class,例如创建日志对象
private final Logger log = LoggerFactory.getLogger(JDBCTest.class);
Java反射机制是指在程序的运行过程中可以构造任意一个类的对象、获取任意一个类的成员变量和成员方法、获取任意一个对象所属的类信息、调用任意一个对象的属性和方法。反射机制使得Java具有动态获取程序信息和动态调用对象方法的能力。可以通过以下类调用反射API。
Class类:可获得类属性方法
Field类:获得类的成员变量
Method类:获取类的方法信息
Construct类:获取类的构造方法等信息
成员内部类:作为成员对象的内部类。可以访问private及以上外部类的属性和方法。外部类想要访问内部类属性或方法时,必须要创建一个内部类对象,然后通过该对象访问内部类的属性或方法。
外部类也可访问private修饰的内部类属性。
局部内部类:存在于方法中的内部类。访问权限类似局部变量,只能访问外部类的final变量。
匿名内部类:只能使用一次,没有类名,只能访问外部类的final变量。
静态内部类:类似类的静态成员变量。
1)使用public修饰符的变量和方法,在任何一个类中创建对象后都可以访问到,包括当前类、同包的类、子孙类以及外包的类。
2)使用protected修饰符的变量和方法,不可以访问外包类创建的对象。
3)不写修饰符的变量和方法,为默认类,只可以访问同一个包中类创建的对象。
4)使用private修饰符的变量和方法,只能在当前类中使用。
| 访问修饰符 | 同一类中 | 同一包中 | 子类中 | 全局 |
|---|---|---|---|---|
| public | 可以 | 可以 | 可以 | 可以 |
| protect | 可以 | 可以 | 可以 | 不可以 |
| 不写默认下 | 可以 | 可以 | 不可以 | 不可以 |
| private | 可以 | 不可以 | 不可以 | 不可以 |
重载和重写是Java中实现多态的重要机制。重载(Overload):又称静态多态,编译时多态: 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重载程序示例如下:
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下两个参数类型顺序不同
public String test(int a, String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test()); o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重写(Override): 又称动态多态,运行时多态:发生在两个类中;方法名、参数、返回类型都与父类一致,不可以修改,但是里面的实现可以与被重写方法不同。(可以使用@Overwrite标签校验),重写示例程序如下:
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
@Override
public void move(){
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}
先后顺序:静态成员变量、成员变量、构造方法。
类未被加载过:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
类加载过: 父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。(因为静态成员变量和静态代码块只加载一次)
(1)为了程序的结构能够更加清晰从而便于维护。假设Java语言支持多重继承,类C继承自类A和类B,如果类A和B都有自定义的成员方法f(),那么当代码中调用类C的f()会产生二义性。Java语言通过实现多个接口间接支持多重继承,接口由于只包含方法定义,不能有方法的实现,类C继承接口A与接口B时即使它们都有方法f(),也不能直接调用方法,需实现具体的f()方法才能调用,不会产生二义性。
(2)多重继承会使类型转换、构造方法的调用顺序变得复杂,会影响到性能。
(1)final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承。
(2)finally作为异常处理的一部分,只能在try/catch语句中使用,finally附带一个语句块用来表示这个语句最终一定被执行,经常被用在需要释放资源的情况下。
(3)finalize是Object类的一个protected方法,在垃圾收集器执行的时候会调用被回收对象的finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用finalize()方法,并在下一次垃圾回收动作发生时真正回收对象占用的内存。
static的主要作用有两个:
(1)为某种特定数据类型或对象分配与创建对象个数无关的单一的存储空间。
(2)使得某个方法或属性与类而不是对象关联在一起,即在不创建对象的情况下可通过类直接调用方法或使用类的属性。
具体而言static又可分为4种使用方式:
(1)修饰成员变量。用static关键字修饰的静态变量在内存中只有一个副本。只要静态变量所在的类被加载,这个静态变量就会被分配空间,可以使用’‘类.静态变量’‘和’‘对象.静态变量’'的方法使用。
(2)修饰成员方法。static修饰的方法无需创建对象就可以被调用。static方法中不能使用this和super关键字,不能调用非static方法,只能访问所属类的静态成员变量和静态成员方法。
(3)修饰代码块。JVM在加载类的时候会执行static代码块。static代码块常用于初始化静态变量。static代码块只会被执行一次。
(4)修饰内部类。static内部类可以不依赖外部类实例对象而被实例化。静态内部类不能与外部类有相同的名字,不能访问普通成员变量,只能访问外部类中的静态成员和静态成员方法。
Java中的数据类型分为基本数据类型和引用数据类型两大类;
基本数据类型4类(整形、浮点型、布尔型、字符型)8种{byte(1个字节)、short(2个字节)、int(4个字节)、long(8个字节)、float(4个字节)、double(8个字节)、boolean(实现虚拟机不同有所差异)}。
引用数据类型(类、接口、数组、枚举类型)。
1字节=16位,可推算出表示范围,有符号需要去掉符号位(即从负数表示到正数,boolean、char类型除外)数据类型字节数确定也是保证Java程序的可移植,跨平台的基础。
char类型可以存储一个中文汉字,Java中默认使用的编码是Unicode(也叫万国码,能表示英文、中文以及其它国家文件和一些特殊符号等等),Unicode编码是能表示汉字的,一个char类型占2个字节(16比特),放一个中文没问题的。
扩展:使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;
不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type),Java 5以后引入的枚举类型也算是一种比较特殊的引用类型。
扩展:String是java.lang包下的一个final类,由Java默认导入,所以不需要写import导入,类似的还有System,Math,基本数据类型包装类。
扩展:String不可变以及不可继承的好处
(1)有当字符串是不可变的,字符串常量池才能实现,字符串池的实现可以在运行时节约很多堆空间,因为不同的字符串变量都指向池中的同一个字符串;
(2)可以避免一些安全漏洞,比如在 Socket 编程中,主机名和端口都是以字符串的形式传入,因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞;
(3)多线程安全,因为字符串是不可变的,所以同一个字符串实例可以被多个线程共享,保证了多线程的安全性;
(4)适合做缓存的 key,因为字符串是不可变的,所以在它创建的时候哈希值就被缓存了,不需要重新计算速度更快,所以字符串很适合作缓存的中的 key。
(1)节省空间:字符串常量存储在JVM的字符串池中可以被用户共享。
(2)提高效率:String会被不同线程共享,是线程安全的。在涉及多线程操作中不需要同步操作。
(3)安全:String常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。
String用于字符串操作,属于不可变类。String对象一旦被创建,其值将不能被改变。而StringBuffer是可变类,当对象创建后,仍然可以对其值进行修改。
String类采用利用final修饰的字符数组进行字符串保存,因此不可变。如果对String类型对象修改,需要
新建对象,将老字符和新增加的字符一并存进去。
StringBuilder,采用无final修饰的字符数组进行保存,因此可变。但线程不安全,但效率比StringBuffer高。
StringBuffer,采用无final修饰的字符数组进行保存,可理解为实现线程安全的StringBuilder,效率比StringBuilder稍差。
一个或者两个。如果在常量池中没有“xyz”的话,就会在常量池中创建一个"xyz"对象,另外面一个是用new创建在堆上的对象;如果在常量池中有“xyz”的话,则在堆上创建一个对象s,指向常量池中的“xyz”。
扩展:(字符串常量池)字符串常量池是存储在 Java 堆内存中的字符串池,是为防止每次新建字符串带的时间和空间消耗的一种解决方案。在创建字符串时 JVM 会首先检查字符串常量池,如果字符串已经存在池中,就返回池中的实例引用,如果字符串不在池中,就会实例化一个字符串放到池中并把当前引用指向该字符串。
对于基本数据类型比较的是值,对于引用类型来说 = = 比较的变量是栈内存中存放的地址信息是否相同,用来判断两个对象的地址是否相同,即是否指向相同的对象。equals在重写了equals方法后比较的是值的信息是否相同。equals方法最先比较的就是hashCode方法是否相同,如果一个类没有重写equals方法,则equals和= =的作用是相同的,这是因为所有的类都继承于Object 超类,而Object超类中的equals方法返回是用==判断的。String对象和Integer对象都重写了equals方法。重写equals方法一定也要重写hashcode方法。
扩展:重写hashCode方法和equals方法的几种方式(当然也可以自己重写)

如下面程序:
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
System.out.println("s1与s2使用==比较结果:" + (s1 == s2));
System.out.println("s1.equals(s2)使用equals比较结果:" + s1.equals(s2));
System.out.println("s2与s3使用==比较结果"+ (s2 == s3));
System.out.println("s2与s3使用equals比较结果" + s2.equals(s3));
}
程序运行结果:(一定要注意基本数据类型包装类中的常用高频数值范围,可能会出现不一样的结果)

Java中只有值传递。Java语言的方法调用只支持参数的值传递(Java中没有指针)。函数调用时,无论值类型变量还是引用类型变量,都是将变量所存储的值copy给了函数的实参,区别在于基本类型变量的值就是类型值本身,而引用类型变量的值是一个地址。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。在这种类型的变量传递给参数的时候:会将变量值(引用地址)copy一份,传递给函数作为实参。也就是说:传递给函数的变量所指向的空间,与函数实参所指向的空间是相同的。因此,在函数中通过实参去改变对象的内容,会影响到函数外部变量所指向的对象的内容,因为它们都指向同一个对象。
数组没有length()方法,有length 的属性。String中通过length方法获取字符串长度。集合如List通过size()方法获取集合的大小。
(1)hashCode:通过对象计算出的散列码。用于map型或equals方法。需要保证同一个对象多次调用该方法,总返回相同的整型值。
(2)equals:判断两个对象是否一致。需保证equals方法相同对应的对象hashCode也相同。
(3)toString: 用字符串表示该对象
(4)clone: 浅拷贝一个对象
对于Java基本数据类型,均对应一个包装类。
装箱就是自动将基本数据类型转换为包装器类型,如int->Integer
拆箱就是自动将包装器类型转换为基本数据类型,如Integer->int
Java是面向对象的程序设计语言,讲究的是万物皆对象的理念。而基本数据类型在某些场景下无法使用,包装类可以向操作其它类一样简单操作对“基本数据类型进行操作”;
包装类提供了更多更实用的方法,如hashCode方法,equals方法以及valueOf方法等等,功能比基本数据类型丰富。从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
public class DataTest {
public static void main(String[] args) {
// 定义了一个字符串,字符串的内容为256
String numberString = "252";
// Integer中的parseInt方法返回值类型为Integer
// 之所以能用int类型去接,是因为自动拆箱,将包装类型拆箱为基本数据类型
int number = Integer.parseInt(numberString);
// number 为基本数据类型,进行加4操作,返回的应该是基本数据类型中的int型
// 之所以能用Integer包装类型去接,是因为自动装箱,将int数据类型自动装箱
Integer hexInteger = number + 4;
// 可以对包装类型赋值像给基本数据类型中的int赋值一样,自动装箱
// int自动装箱调用了Integer中的valueOf方法(请注意此方法)
// 下面等同于 Integer equalsInteger = Integer.valueOf(256);
Integer equalsInteger = 256;
// 这里很容易理解,==比较的是两个对象的地址,hexInteger和equalsInteger
// 是两个不同的Integer对象,他们的地址是不同的,==比较结果是false
// 比较结果为false,但是如果我们将数换为64呢?==比较的结果如何???
System.out.println(hexInteger == equalsInteger);
// 包装类型都重写了equals方法,所以这里比较的是两个对象的值内容 System.out.println(hexInteger.equals(equalsInteger));
// 将数字转化为16进制字符串
System.out.println(Integer.toHexString(hexInteger));
}
}
程序运行结果如下:

在上述程序示例中的一个问题,如果我们换成64呢?结果还是一样吗?
public class DataTest {
public static void main(String[] args) {
// 定义了一个字符串,字符串的内容为60
String numberString = "60";
// Integer中的parseInt方法返回值类型为Integer
// 之所以能用int类型去接,是因为自动拆箱,将包装类型拆箱为基本数据类型
int number = Integer.parseInt(numberString);
// number 为基本数据类型,进行加4操作,返回的应该是基本数据类型中的int型
// 之所以能用Integer包装类型去接,是因为自动装箱,将int数据类型自动装箱
Integer hexInteger = number + 4;
// 可以对包装类型赋值像给基本数据类型中的int赋值一样,自动装箱
// int自动装箱调用了Integer中的valueOf方法(请注意此方法)
Integer equalsInteger = 64;
// 比较结果为false,但是如果我们将数换为64呢?==比较的结果如何???
System.out.println(hexInteger == equalsInteger);
// 包装类型都重写了equals方法,所以这里比较的是两个对象的值内容
System.out.println(hexInteger.equals(equalsInteger));
// 将数字转化为16进制字符串 System.out.println(Integer.toHexString(hexInteger));
}
}
程序输出结果如下:

这里改为64以后,为什么==比较也是true了呢?
这里就需要对装箱的实现以及装箱的概念有所了解。当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,看看valueOf方法的源码就知道了,valueOf方法源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里面有一个IntegerCache是Integer的内部类,其代码如下:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,超出这个范围的数值才会真正的new一个对象出来。所以上面的两段程序的输出结果是不一样的。
扩展:基本数据类型包装类中的Byte、Short、Integer、Long的高频缓存范围为-128到127;Character的高频缓存为-128到127;Float、Double没有高频缓存区。Integer是唯一一个可以修改高频缓存范围的包装类。通过在VM optons中如下设置:-XX:AutoBoxCacheMax=8866 即修改缓存最大值为8866。
再看如下程序:
public class DataTest {
public static void main(String[] args) {
int a = 100;
Integer b = new Integer(100);
Integer c = new Integer(100);
System.out.println(a == b);
System.out.println(a == c );
System.out.println(b == c);
System.out.println(b.equals(a));
System.out.println(c.equals(a));
System.out.println(b.equals(c));
}
}
程序运行结果如下:

a与b、a与c使用= =比较,会将Integer包装类自动拆箱为基本数据类型中的int,进行值比较与a与b、a与c、b与c使用equals(包装类重新了equals方法)方法一样进行的都是值比较,所以是true;而b与c使用==进行比较的结果却为false,这是因为new出来的对象不会使用高频缓存范围的数值,是先创建对象,这两个对象是不同的对象,所以地址是不同的,返回false;(当然这么写代码,如果你的编程软件安装了阿里代码开发检测工具的时候是会有黄色警告的)
包装类型比基本数据类型的应用范围更广,同时提供了很多方法,很方便,一般情况下确定是使用基本数据类型还是包装类型原则如下:
1)所有的POJO类属性必须使用包装类;
2)RPC中的方法返回值和参数必须使用包装类;
3)所有局部变量推荐使用基本数据类型;
泛型不能使用基本数据类型。泛型在 JVM(Java虚拟机)编译的时候会类型檫除,比如代码 List list 在 JVM 编译的时候会转换为 List list ,因为泛型是在 JDK 5 时提供的,而 JVM 的类型檫除是为了兼容以前代码的一个折中方案,类型檫除之后就变成了 Object,而 Object 不能存储基本数据类型,但可以使用基本数据类型对应的包装类,所以像 List list 这样的代码是不被允许的,编译器阶段会检查报错,而 List list 是被允许的。
泛型,即“参数化类型”,解决不确定对象具体类型的问题。在编译阶段有效。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型在类中称为泛型类、接口中称为泛型接口和方法中称为泛型方法。
Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程被称为泛型擦除。
不对,如果两个对象x和y满足x.equals(y) = = true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
扩展:equals方法介绍:首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。
实现高质量的equals方法的诀窍包括:1. 使用==操作符检查"参数是否为这个对象的引用";2. 使用instanceof操作符检查"参数是否为正确的类型";3. 对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;4. 编写完equals方法后,问自己它是否满足对称性、传递性、一致性;5. 重写equals时总是要重写hashCode;6. 不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
有问题,不能通过编译;在Java中没小数点的默认是int,有小数点的默认是double。将double转换为float需要进行强制类型转换,如 float f = (float)5.2;或者float f = 5.2f; 表示这是一个float类型的数据。
扩展:隐式转换,即自动转换,它不要求加以声明,是系统默认的。它是由小至大的转换:
byte——>short——>int——>long——>float——>double
char——>int——>long——>float——>double
显式转换,即强制转换,它是由大至小的转换,它的格式为:
类型 变量=(类型) 变量2;
Java规范有这样的规则:1.高位转低位需要强制转换 2.低位转高位自动转.在Java中没小数点默认的为int类型,1是int 型,s1是 short型,通过加1运算后s1 自动转为int 型,所以要强制转换,改为 s1 = (short)s1 + 1;
short s1 = 1; s1 += 1;正确
这个不会发生类型的提升,JAVA规范上说 :
e1+=e2 实际上是 e1=(T1)(e1+e2) ,其中T1是e1的数据类型。
s1+=1等效于 s1=(short)(s1+1),所以是正确的。
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行向下取整。(这其实就是简单的数学概念,明白四舍五入就错不了)
比较结果为false。浮点数在计算机中不能准确的表示出来,30.1结果输出为0.30000000000000004;
扩展:有的人说在0.1改为0.1f结果就为true了,这里是错误的,因为30.1f结果为float,结果为0.3没问题,但是与0.3(小数类型没有显式定义数据类型的话,默认是double)比较,会将float的0.3隐世转换为double类型,还是0.30000000000000004与0.3比较,所以还是false;
测试程序如下:
public static void main(String[] args) {
float a = 1.2f;
double b = a;
System.out.println(a);
System.out.println(b);
System.out.println(3*0.4f);
System.out.println(1.2);
System.out.println( (float)(3*0.4)== 1.2);
}
程序输出结果:

扩展:有的人说在0.1改为0.1f结果就为true了,这里是错误的,因为3*0.1f结果为float,结果为0.3没问题,但是与0.3(小数类型没有显式定义数据类型的话,默认是double)比较,会将float的0.3隐世转换为double类型,还是0.30000000000000004与0.3比较,所以还是false;
测试程序如下:
public static void main(String[] args) {
float a = 1.2f;
double b = a;
System.out.println(a);
System.out.println(b);
System.out.println(3*0.4f);
System.out.println(1.2);
System.out.println( (float)(3*0.4)== 1.2);
}
程序输出结果:

持续更新中,如有错误,欢迎指正!