今天元宵节,祝大家元宵快乐。
Java面向对象又进一步理解了很多。Java的抽象类和接口有很多相似的地方也有很多不同的地方,总之,用法和目的也不尽相同。Java基本数据类型和它们的包装类有自动装箱和拆箱。Java对于不知道传入的数据类型可以用泛型,泛型类,泛型方法,泛型数组。
知识点及习题
5.Java支持的数据类型有哪些?什么是自动拆装箱?
Java语言支持的8中基本数据类型是:
byte
short
int
long
float
double
boolean
char
基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装成为装箱,解包装称为拆箱)
代码如下:
//声明一个Integer对象
Integer num = 10;
//以上的声明就是用到了自动的装箱:解析为
Integer num = new Integer(10);以上就是一个很好的体现,因为10是属于基本数据类型的,原则上它是不能直接赋值给一个对象Integer的,但jdk1.5后你就可以进行这样的声明,这就是自动装箱的魅力
自动将基本数据类型转化为对应的封装类型。成为一个对象以后就可以调用对象所声明的所有的方法
自动拆箱:故名思议就是将对象重新转化为基本数据类型:
//装箱
Integer num = 10;
//拆箱
int num1 = num;自动拆箱有个很典型的用法就是在进行运算的时候:因为对象时不恩直接进行运算的,而是要转化为基本数据类型后才能进行加减乘除
Integer num = 10;
//进行计算时隐含的有自动拆箱
System.out.print(num--);哈哈 应该感觉很简单吧,下面我再来讲点稍微难点的,
//在-128~127 之外的数
Integer num1 = 297; Integer num2 = 297;
System.out.println("num1==num2:"+(num1==num2));
// 在-128~127 之内的数
Integer num3 = 97; Integer num4 = 97;
System.out.println("num3==num4:"+(num3==num4)); 打印的结果是:num1==num2: false num3==num4: true
很奇怪吧:这就归结于java对于Integer与int的自动装箱与拆箱的设计,是一种模式:叫享元模式(flyweight)
为了加大对简单数字的重利用,java定义:在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象
而如果超过了从–128到127之间的值,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个 Integer对象;明白了吧
以上的现象是由于使用了自动装箱所引起的,如果你没有使用自动装箱,而是跟一般类一样,用new来进行实例化,就会每次new就都一个新的对象;
这个的自动装箱拆箱不仅在基本数据类型中有应用,在String类中也有应用,比如我们经常声明一个String对象时:
复制代码代码如下:
String str = "sl";
//代替下面的声明方式
String str = new String("sl");
基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。虽然为您打包基本数据类型提供了方便,但提供方便的同时表示隐藏了细节,建议在能够区分基本数据类型与对象的差别时再使用。
autoboxing和unboxing
在Java中,所有要处理的东西几乎都是对象 (Object),例如之前所使用的Scanner是对象,字符串(String)也是对象,之后还会看到更多的对象。然而基本(Primitive)数据类型不是对象,也就是您使用int、double、boolean等定义的变量,以及您在中直接写下的字面常量。
在前一个小节中已经大致看到了操作对象的方便性,而使用Java有一段时间的人都知道,有时需要将基本数据类型转换为对象。例如使用Map对象要put()方法时,需要传入的参数是对象而不是基本数据类型。
要使用打包类型(Wrapper Types)才能将基本数据类型包装为对象,前一个小节中您已经知道在J2SE 5.0之前,要使用以下语句才能将int包装为一个Integer对象:Integer integer = new Integer(10);
在 J2SE 5.0之后提供了自动装箱的功能,您可以直接使用以下语句来打包基本数据类型:Integer integer = 10;
在进行编译时,编译器再自动根据您写下的语句,判断是否进行自动装箱动作。在上例中integer参考的会是Integer类的实例。同样的动作可以适用于 boolean、byte、short、char、long、float、double等基本数据类型,分别会使用对应的打包类型(Wrapper Types)Boolean、Byte、Short、Character、Long、Float或Double。下面直接使用自动装箱功能来改写范例 4.4。
范例4.5 AutoBoxDemo.java
复制代码代码如下:
public class AutoBoxDemo {
public static void main(String[] args) {
Integer data1 = 10;
Integer data2 = 20;
// 转为double值再除以3
System.out.println(data1.doubleValue() /3);
// 进行两个值的比较
System.out.println(data1.compareTo(data2));
}
}
程序看来简洁了许多,data1与data2在运行时就是Integer的实例,可以直接进行对象操作。的结果如下:
3.3333333333333335
–1
自动装箱运用的方法还可以如下:
复制代码代码如下:
int i = 10;
Integer integer = i;
也可以使用更一般化的java.lang.Number类来自动装箱。例如:
Number number = 3.14f;
3.14f会先被自动装箱为Float,然后指定给number。
从J2SE 5.0开始可以自动装箱,也可以自动拆箱(unboxing),也就是将对象中的基本数据形态信息从对象中自动取出。例如下面这样写是可以的:
复制代码代码如下:
Integer fooInteger = 10;
int fooPrimitive = fooInteger;
fooInteger引用至自动装箱为Integer的实例后,如果被指定给一个int类型的变量fooPrimitive,则会自动变为int类型再指定给fooPrimitive。在运算时,也可以进行自动装箱与拆箱。例如:
复制代码代码如下:
Integer i = 10;
System.out.println(i + 10);
System.out.println(i++);
上例中会显示20与10,编译器会自动进行自动装箱与拆箱,也就是10会先被装箱,然后在i + 10时会先拆箱,进行加法运算;i++该行也是先拆箱再进行递增运算。再来看一个例子:
复制代码代码如下:
Boolean boo = true;
System.out.println(boo && false);
同样的boo原来是Boolean的实例,在进行AND运算时,会先将boo拆箱,再与false进行AND运算,结果会显示false。
//
装箱:从基本类型转换成Object类型,称之为装箱;***拆箱:从Object转换乘基本类型的操作,称之为拆箱。这个操作在反射过程中用的比较的多。
装箱:在堆中建立一个Object实例,把你指定的值复制成去;***拆箱:判别引用指向的堆中信息是否是要拆成的类型,是取出堆中值送给栈中变量,否则报异常
///
装箱是值类型到object类型或到该值类型所实现的任何接口类型的隐士转换。
将一个值类型装箱会分配一个对象实例并将该值复制到新的对象中。
复制代码代码如下:
int i=123;
object o=i;
这句话的结果是在堆栈上创建一个对象o,而该对象在堆上引用int类型的值。该值是赋值给变量i
的值类型值的一个副本。
下面是显示执行装箱转换
复制代码代码如下:
int i=123;
ojbect o=(object)i;
此例将整数变量i通过装箱转换为对象o。这样,存储在变量i中的值就从123改为456。此例显示对象保留了内容的原始副本。即123。
取消装箱是从object类型到值类型或从接口类型到实现该接口的值类型的显示转换。取消装箱操作包括:
检查对象实例,确保它是给定值类型的一个装箱值。
将该值从实例复制到值类型变量中。
例子:
复制代码代码如下:
int i=123;
object box=i;
int j=(int)box;
9.接口和抽象类的区别是什么?
程序设计所面对的问题域——客观世界,是由许多事物构成的,这些事物既可以是有形的(比如一辆汽车),也可以是无形的(比如一次会议)。把客观世界中的事物映射到面向对象的程序设计中就是对象。对象是面向对象程序设计中用来描述客观事物的程序单位。客观世界中的许多对象,无论其属性还是其行为常常有许多共同性,抽象出这些对象的共同性便可以构成类。所以,类是对象的抽象和归纳,对象是类的实例。
抽象方法:仅有定义,没有具体实现的方法
抽象类:含有抽象方法的类
一个类如果被定义为抽象类,它就不能被实例化,也就是说,不能有自己的对象
声明接口使用关键字“interface”
接口中一般只包含一组public抽象方法(且必须是公有抽象方法,但方法定义中public abstract可省略),也可以包含public 静态final数据
Java不允许一个子类继承多个超类,却允许一个子类继承一个超类并实现多个接口.
给你举个例子吧。比如一个公司,有老板,老板聘的经理,还有员工,
类就是员工,抽象类就是经理,接口就是老板。
接口里就是给个方法,但是他自己不做,比如老板说我要那个文件,给我定个机票,我要那个策划方案等,都是下面人做。老板只说不做。
抽象类给的方法,有的他自己做,有的其他人做。比如经理说我要那个文档,员工就要发给他,但是他自己也要做点事,比如拿方案给老板看。经理又说又做。
一般类给的方法,就是什么都要做,都要实现。属于不说必须做的。
34.接口是否可继承接口?接口类是否实现接口?接口类是否可继承实体类?
接口是可以继承接口的
面试常考题
Java抽象类可以有构造函数吗?
抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。
在继承了抽象类的子类中通过super(参数列表)调用抽象类中的构造方法
Java抽象类可以实现接口吗?它们需要实现所有的方法吗?
可以
不需要,抽象类可以不实现接口的方法,由抽象类的子类实现。
Java抽象类可以是final的吗?
Java抽象类不可以被 final修饰
抽象类需要被继承才能使用,而被final修饰的类无法被继承,所以abstract和final是 不能共存的。
Java抽象类可以有static方法吗?
Java抽象类中不能有静态的抽象方法。
原因:抽象类是不能实例化的,即不能被分配内存;而static修饰的方法在类实例化之前就已经别分配了内存,这样一来矛盾就出现了:抽象类不能被分配内存,而static方法必须被分配内存。所以抽象类中不能有静态的抽象方法。
另外,定义抽象方法的目的是重写此方法,但如果定义成静态方法就不能被重写。
可以创建抽象类的实例吗?
不可以
抽象类必须有抽象方法吗?
抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。
如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。
Java抽象类和接口有何不同?
1、抽象类是类,它的子类不能再继承其它类了,但可以实现一个和多个接口。接口不是类,它的子接口可以继承多个接口。
2、抽象类中是可以有不用abstract修饰的方法,而接口中只能有抽象方法,即方法都要用abstract修饰。抽象类中除了能定义抽象方法以外,也可以定义具体的方法,并且可以定义方法体内容.接口中是不可以定义具体的方法实现,他只能允许你定义方法但是不能有任何方法体.
3、抽象类可以实现接口,而接口是不能继承或实现抽象类的。
何时选用抽象类而不是接口?
接口是一种协定,抽象类则相当于类模板。
使用抽象类,而不要使用接口来分离协定与实现。
如果需要提供多态层次结构的值类型,使用接口。
如果一个类型必须实现多个协定,或者协定适用于多种类型,使用接口。
虽然抽象类和接口都支持将协定与实现分离开来,但接口不能指定以后版本中的新成员,而抽象类可以根据需要添加成员以支持更多功能。
优先考虑定义类,而不是接口。
1.写一个形状接口Shape 有两个方法一个求周长,一个求面积 2. 写一个长方形类Rect继承于形状类 增加属性长和宽 分别去覆盖求周长和求面积的方法 3.写一个圆形类Circle 增加属性半径 分别去覆盖求周长和求面积的方法 写一个测试类,在测试类中分别创建不同的对象放入进一个Shape 数组中进行,循环数组中的元素求周长和面积
package com.jiale.demo01;
public interface Shape {
void Perimeter();
void Area();
}
package com.jiale.demo01;
public class Rect implements Shape {
public Rect(int length, int wide) {
// TODO自动生成的构造函数存根
setLength(length);
setWide(wide);
}
private int length;
private int wide;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getWide() {
return wide;
}
public void setWide(int wide) {
this.wide = wide;
}
@Override
public void Perimeter() {
// TODO自动生成的方法存根
System.out.println("长方形长为:"+this.length+" 宽为: "+this.wide+" 它的周长是:"+((this.length*2)+(this.wide*2)));
}
@Override
public void Area() {
// TODO自动生成的方法存根
System.out.println("长方形长为:"+this.length+" 宽为: "+this.wide+" 它的面积是:"+(this.length*this.wide));
}
}
package com.jiale.demo01;
public class Circle implements Shape {
public Circle(double radius) {
// TODO自动生成的构造函数存根
setRadius(radius);
}
private double radius;
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public void Perimeter() {
// TODO自动生成的方法存根
System.out.println("圆的半径为:"+this.radius+" 它的周长是:"+(2*3.14*this.radius));
}
@Override
public void Area() {
// TODO自动生成的方法存根
System.out.println("圆的半径为:"+this.radius+" 它的面积是:"+(3.14*this.radius*this.radius));
}
}
package com.jiale.demo01;
public class demo01 {
public static void main(String[] args) {
// TODO自动生成的方法存根
Shape[] shape = new Shape[2];
Rect a = new Rect(2,3);
Circle b = new Circle(3);
shape[0] = a;
shape[1] = b;
for(int i=0;i<2;i++){
shape[i].Perimeter();
shape[i].Area();
}
}
}