java-静态块-包装类-和equals方法-抽象类-接口-内部类
java-静态块-包装类-==和equals方法-抽象类-接口-内部类
静态块
初始化块,有两种
普通初始化块
在有对应的类被实例化时,即new关键字调用它的构造函数的时候,new一次僦会执行一个普通初始化块,可以执行n次。
public class StaticBlock {
public static void main(String[] args) throws ClassNotFoundException {
//Class.forName("cn.ybzy.javabasic.Test");
new Test();
new Test();
new Test();
}
}
class Test{
//实例化时,被执行一次,可执行多次。
{
System.out.println("普通初始化静态块");
}
//只要类一加载到jvm中就执行,并只执行一次。
static {
System.out.println("静态的初始化块!");
}
}
-----------------------------------------------------------------------------
静态的初始化块!
普通初始化静态块
普通初始化静态块
普通初始化静态块
静态的初始块,即有static修饰的
static代码块
static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载内存中的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
public class StaticBlock {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("cn.ybzy.javabasic.Test");//作用就是将参数指定的类加载到jvm
}
}
class Test{
//实例化时, 即new 调用类构建造器时,才被执行一次,可执行多次。
{
System.out.println("普通初始化静态块");
}
//只要类一加载到jvm中就执行,并只执行一次。当然了,创建对象肯定先加载吗,更会执行一次静态块了,但不管怎样只执行一次
static {
System.out.println("静态的初始化块!");
}
}
-----------------------------------------------------------------------
静态的初始化块!
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。下面看个例子:
public class StaticDemo {
static {
int age =32;
System.out.println("age的值为:"+age);
}
public static void main(String args[]) {
new StaticDemo();
new StaticDemo();
}
}
------------------------------------
age的值为:32
在继承中的,执行顺序
包装类
什么是包装类
包装类(Wrapper Class): Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,为了使用方便和解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八种基本数据类型对应的类统称为包装类(Wrapper Class),包装类均位于java.lang包。
包装类和基本数据类型的转换
为了使用方便Java中将8种基本数据类型进行了封装:除了Integer和Character类以外,其它六个类的类名和基本数据类型一直,只是类名的第一个字母大写即可。
boolean —> Boolean
char —> Character
byte—> Byte
short—> Short
long—> Long
int —> Integer
float—> Float
double—> Double
int和integer类之间的转换
在实际转换时,使用Integer类的构造方法和Integer类内部的intValue方法实现这些类型之间的相互转换:
int n=5;
Integer n1=new Integer(n);
System.out.println("int类型转换为integer类:"+n1);
Integer i=new Integer(50);
int i1 = i.intValue();
System.out.println("integer类转换为int类型:"+i1);
自动拆装箱
JDK自从1.5版本以后,就引入了自动拆装箱的语法,也就是在进行基本数据类型和对应的包装类转换时,系统将自动进行,这将大大方便程序员的代码书写。
自动装箱:将 基本数据类型 封装为对象类型,来符合java的面向对象的思想。
自动拆箱:将对象重新转化为基本数据类型。
//5是基本数据类型,通过自动装箱变成对象类型。
Integer iii=5; //编译器执行了Integer iii = Integer.valueOf(5)
int iii2=iii; //自动拆箱,实际上执行了 int iii2 = iii.intValue()
System.out.println(iii2);
valueOf()源码分析:
public static Integer valueOf(int i) {
assert IntegerCache.high>= 127;
if (i >= IntegerCache.low&& i <= IntegerCache.high)
return IntegerCache.cache[i+ (-IntegerCache.low)];
return new Integer(i);
}
说明:Integer iii=5;相当于编译器执行了Integer iii = Integer.valueOf(5)操作。这个方法就是返回一个 Integer 对象,只是在返回之前,看作了一个判断,判断当前 i 的值是否在 [-128,127] 区间,且 IntegerCache 中是否存在此对象,如果存在,则直接返回引用,否则,调用构造器执行类型转换!
包装类的缓存值
各个包装类缓存值范围 :
boolean:true和false
byte:-128~127
char:0~127
short:-128~127
int:-128~127
long:-128~127
特别注意:对于float和double没有缓存。
转成包装类有什么好处呢?
1、首先包装类是一种类,类就有定义好的属性或方法,这些方法对应就完成了一些特定的功能,我们可以调用这些方法完成一些工作,简化了我们自己的编码量,没有这些方法,我们需要这些功能时就要自己编程去实现。
2、基本数据类型都用隐式的默认值,不是类的对象,不能为null,很多时候我们可能需要我们的这些类型可以为null,这时就需要用包装类了!
==和equals方法
我们创建对象时,引用变量名放在栈内存中,而真实内容放在堆内存中,所以我伙xxx.toString();输出的是以@开头的地址,该地址指向堆内存中存储对象的地方。
基本数据类型的值,直接放在栈内存中,所以println打印的是真实内容值。
常量放在内存的常量池中,如:String s1=“xiongshaowen”;String s2=“xiongshaowen”;这里两个s1,s2字符串常量是完全相等的。
用来判断两个变量是否相等,如果是基本类型,则只要值相等,判断的结果就是true,例如65和‘A’比较,结果就是true。
如果是比较的引用类型,那么只有在两个变量指向的是同一对象时才是true,例如String a=“111”,String a1=new String(“111”);他们 比较的结果就是false,因为他们是两个对象。
如果想要让两个只要内容相同的对象比较结果就是true,那么可以使用equals来比较,但是需要对equals进行重写。
equals默认情况下内部就是 比较,所以如果不重写的话,比较结果和 比较一样,注意在String中已经重写了equals方法,所以我们比较字符串的内容是否相等一般都是用equals方法。
其他的引用类型的变量比较就要重写equals方法来自定义相等的规则,所以不仅仅是能实现上述的情况,还可以让不相等的对象相等,是看怎么写了。
正确重写equals应遵循的规则:
1.对于任意x,x.equals(x)结果要是true;自反性
2.对于任意x、y,如果x.equals(y)是true,那么y.equals(x)也应该是true;对称性
3.对于任意的x、y、z,如果x.equals(y)是true,y.equals(z)是true,那么x.equals(z)也应该是true;传递性
4.对于任意x、y,若对象中用于等价比较的信息未变,那么无论调用多少次x.equals(y)的结果应该是同样的,要么都是true要么都是false;一致性
5.对于任何非null的对象,x.equals(null)一定是false;
重写equals方法的时候,一般要用到instanceof和强制类型转换,然后当类型匹配时再进行比较。
一个经典的重写equals的例子:
public class Bridge{
xxxxxxxxxxx
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Bridge))
return false;
Bridge other = (Bridge) obj;
if (id != other.id)
return false;
else false;
return true;
}
}
例:Person类,三个成员变量,创建两个对象来比较是不是同一个对象
Person.java *
public class Person {
private int id; //这个我们设置独一无二的,
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
//if (this.getClass() != obj.getClass())
if(!(obj instanceof Person))
return false;
Person other = (Person) obj;
/*if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;*/
//上面用系统自动重定的方法,将是十分麻烦,假设有几十个字段,那不要命
//这里我们有一个id,一般来说是独一无二的,我们只比较id即可。
if(this.getId() ==other.getId()) {
return true;
}else {
return false;
}
}
}
EqualsOverridTest.java 测试类
public class EqualsOverrideTest {
public static void main(String[] args) {
Person person1 = new Person();
Person person2= new Person();
person1.setId(1);
person2.setId(6);
System.out.println("person1.equals(person2):::"+person1.equals(person2));
}
}
--------------------------------------------------------------------------
person1.equals(person2):::false
抽象类抽象方法
第一个问题:抽象类有什么用?它存在的意义是什么?
这回答这个问题之前,先看一下动物界里的一个例子:首先,有一个父类Animal,接着有两个子类,分别是鸟Bird和狗Dog,如下:
public class Animal{
public void bark(){}
}
public class Bird extends Animal{
public void bark(){
System.out.println("唧唧~唧唧~");
}
}
public class Dog extends Animal{
public void bark(){
System.out.println("汪汪~汪汪~");
}
}
可以看到,父类Animal有一个叫唤的方法bark(),两个子类都继承了这个方法,并进行了重写,Bird是唧唧叫,Dog是汪汪叫,现在的问题是Animal怎么叫?它的bark()方法体里应该输出什么样的叫声,是“汪汪”还是“唧唧”?
显然,动物是个抽象的集合名词,我们并不知道动物Animal怎么叫,所以,bark()方法在父类中实现不了,或者说
实现了没有任何意义
,bark()方法只能在子类中根据具体情况去实现。 这样的话就可以把父类Animal中的bark()
方法声明为abstract抽象方法,此时这个类也成了abstract抽象类
。
抽象类意义有三点
:
1.就像上面说的,这样弄“实现了没有任何意义”;
2.Java里面不鼓励函数体的内容为空;
3.用法上子类继承父类后,子类会被强制重写父类中的抽象方法,起到一个提醒和约束的作用。
第二个问题:抽象类怎么用?表现形式是什么样的?
这个问题相对简单,就是语言设计者的一些规定,Java中规定用abstract来修饰抽象方法和抽象类。上面的Animal类写成如下形式:
public abstract class Animal{
public abstract void bark(); //方法前用了abstract修饰,就不能作{}来定义方法体了。
}
第三个问题:抽象类在用的过程中有哪些关键点?
抽象类并不是只能包含象方抽法,他也可以包含普通的成员方法和成员变量。它和普通类的区别主要有三点:
1.抽象类中的抽象方法只能用public或protected修饰。因为,抽象方法来到世间就是为了让子类继承重写的,而private的方法不能被子类继承,显然矛盾。
2.抽象类不能创建对象,即不能实例化。因为,抽象类中包含没有实现的抽象方法,是不完整的,所以不能用来创建对象。(有一种特殊情况,就是一个类中并没有抽象方法,但是类class有abstract修饰,被声明为抽象类,那么这个类也是抽象类,也不能实例化。)
3.如果一个类继承于一个抽象类,那么子类必须实现父类的抽象方法。否则,子类继承的方法还是抽象方法,子类也变成了抽象类,要用abstract修饰。
在其他方面,抽象类和普通类并无区别。
abstract class Animal {
private int i=100;
public abstract void bark();
public void say() {
System.out.println("我在抽像类可以定义普通方法和成员变量i,i为:"+i);
}
}
class Bird extends Animal{
@Override
public void bark() {
System.out.println("小鸟是叽叽,渣渣的叫!");
}
}
public class MainTest {
public static void pls(Animal animal) {
animal.bark();
}
public static void main(String[] args) {
Bird bird = new Bird();
bird.bark();
bird.say();
pls(bird); //这不是创 建Animal对象,这是向下转型了
//System.out.println("不能创建抽象类对象,所以引用父类变量i是不得行的"+bird.i); //不能
}
}
------------------------------------------------------------------
小鸟是叽叽,渣渣的叫!
我在抽像类可以定义普通方法和成员变量i,i为:100
小鸟是叽叽,渣渣的叫!
接口
接口是什么?有什么意义?
接口(英文:Interface) ,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,但基本没什么意义,在我的开发经验里还没有在实际项目中在接口声明变量的应用。
通俗来讲,我们前面学习了抽象类,理解了抽象类,在看接口,就要容易理解一些了,我们可以这样理解接口,抽象类里可以有不实现的方法(抽象方法,存在的目的是让继承抽象类的子类去实现的),当然也可以有普通实现了的方法,那么接口呢?它里面的所有的方法都必须是抽象方法,所以我们可以把接口理解为比抽象类更加抽象的一种类型,它的存在就是为了给实现接口的“子类”来到世间的!
接口的实现语法,在Java中,定一个接口的形式如下:
[public] interface InterfaceName {
int LENGHT=10; //成员变量必须初始化
String PUBLICMSG_ROUND = "】⊿";
public void method(); //抽象方法,默认就是abstract,所以直接这样写就好了
}
注意接口有关的关键点
在jdk1.8以后增加default方法。default修饰的方法是可以在接口的实现类中不实现的,这样就可以添加新的方法,不寮现不报红了。
要让一个类遵循某组特定的接口需要使用implements关键字,具体格式如下:
class ClassName implements Interface1,Interface2,[....]{
defalut void test(){} //jdk1.8以后的新特性
}
可以看出,允许一个类遵循多个特定的接口。如果一个非抽象类遵循了某个接口,就必须实现该接口中的所有方法。对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法。
例:定义一个接口Usb,定义一个类Phone实现接口,定义一个Computer类,用Usb接口连接上Phone 规定传输数据长度为10字符,公聊信息前后缀
Usb.java接口
public interface Usb {
int PROTOCOL_LEN =2;
String PUBLICMSG_ROUND = "】⊿"; //公聊消息的前后缀
public void install();
public void work();
}
Phone.java
public class Phone implements Usb{
@Override
public void install() {
System.out.println("为手机安装USB驱动!");
}
@Override
public void work() {
System.out.println("为手机传输数据!");
}
}
Computer.java
public class Computer {
public void plugin(Usb usb) {
usb.install();
usb.work();
}
}
Test.java测试
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
Phone phone = new Phone();
computer.plugin(phone);
System.out.println(Usb.PUBLICMSG_ROUND);
}
}
--------------------------------------------------------------------------------
为手机安装USB驱动!
为手机传输数据!
】⊿
内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
public class OuterClass {
private String name ;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
class InnerClass{ //内部类的定义
public InnerClass(){
name = "chenssy";
age = 23;
System.out.println(getName());
}
}
}
在这里InnerClass就是内部类,对于初学者来说内部类实在是使用的不多,但是随着编程能力的提高,我们会领悟到它的魅力所在,它能够更加优雅的设计我们的程序结构,手机的安桌编程时会用到大量的内部类。学习使用内部类,我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处。
在内部类可以直接访问外部类的属性和方法,包括私有的。
编译后会产生两个字节码文件
为什么要使用内部类?
在《Java编程思想》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用一个接口很难解决的问题,这个时候我们可以利用内部类来继承多个接口或类来解决这些程序设计问题。可以这样说,一个接口只是解决了部分问题,而内部类使得可以多继承很多接口或父类,从而能解决全部问题。
public interface Father {
}
public interface Mother {
}
//当然接口本身就可以实现多继承
public class Son implements Father, Mother {
}
//上面的写法,我们也可以用内部类的设计方式来实现
public class Daughter implements Father{
class Mother_ implements Mother{
}
}
其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能体会到内部类实现多重继承的好处,因为每个类只能有一个父类。
其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Java的编程思想》):
- 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其外围对象的信息相互独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问。
内部类的基本语法,java代码中怎么实例化内部类,它和外围类怎么联系?
在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素,哪怕是private修饰的元素。
public class InnerClassTest {
private String name ;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
class InnerClass{ //内部类的定义
public InnerClass(){
name = "chenssy";
age = 23;
System.out.println(getName());
}
public void printlnOut() {
System.out.println("姓名:"+getName());
System.out.println("年龄:"+getAge());
}
}
public static void main(String[] args) {
InnerClassTest out = new InnerClassTest();
InnerClassTest.InnerClass inner=out.new InnerClass();
inner.printlnOut();
}
}
-----------------------------------------------------------------------
chenssy
姓名:chenssy
年龄:23
到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。