java升级(一)-面向对象三大特性

java的三大特性

封装

封装就是信息隐藏,利用抽象数据类型将数据和基于数据操作封装在一起,使其构成不可分割的独立实体,数据被保护在抽象数据类型内部,尽可能隐藏内部细节,只保留一些对外接口使之与外部发生关系。系统的其他的对象只能通过包裹在外面的的已经授权的操作来与这个封装对象进行交流和交互。就是用户不需要知道内部细节可以通过对象对外的接口来访问对象。对于封装来说,一个对象他所封装的是自己的属性和操作,所以他不需要外部对象就可以完成自己的操作。

封装的好处 1、良好的封装减少耦合;2、类内部的结构可以自由修改;3、可以对成员进行精确地控制;4、隐藏信息,实现细节;封装可以是我们容易修改类内部的实现细节,而无需修改使用了该类的客户代码,好处就是可以对成员进项更精确的控制。封装又可以对对象的出口做出很好的控制(比如对年龄的输入控制输出限定信息,数据库中一般是0,1方式存储,但展示的时候不能是0,1)我们只需要在get方法里做一下转换。

继承

继承的理念:

        是复用代码将共同的属性和行为尽量避免再次重写,继承是利用已定义的类作为基础建立新的类的技术,新的类可以增加新的数据和新的功能,也可以用父类的功能,但不能选择性的继承父类,通过继承方便复用代码,提高开发的效率。继承描述的是“is-a”的关系,两类之间是继承关系,实际上继承者是被继承者的特殊化,除了拥有被继承者的特性外,还拥有自己独特的特性,继承关系中继承者可以完全替换被继承者,反之不行,(猫是动物,反过来动物世界猫不可以)向上转型。继承定义了类如何互相关联,共享特性,对于若干个相同的类,我们可以抽象出共有的属性和行为定义超类。

使用继承要记住:

1、子类拥有父类的非private得属性和方法;2、子类可以拥有自己的属性和方法即子类对父类的扩展。3、子类可以用自己的方式实现父类。继承中的三个关键东西:构造器、protected关键字、向上转型继承中除了private关键字修饰的还有一个就是构造器,构造器只能被调用不能被继承,调用父类的构造器用super()即可。子类继承会默认调用父类的构造器,但是如果没有默认的父类的构造器子类必须显示的指定父类的构造器,而且是子类构造器中做的第一件事。protected的作用是我们想要尽可能的隐藏内部细节但是我们仍允许子类访问他。但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的。最好的方式是将属性定义为private通过protected方法控制继承者的访问属性。

 继承存在的缺陷:

1、父类变子类也必须变。2、继承破换了封装对父类而言他的实现细节对子类来说是透明的。3、继承是一种强耦合关系。

 继承:继承是面向对象一个重要的概念,达到非常高效的代码重用,但也造成语言执行效率降低,类之间通过extends实现继承。在实现继承时默认会将基类的一个对象传给一个子对象给子类,子类需要对子对象初始化,那么就要调用父类的构造函数,这些都是隐式进行的,在C++中构造函数的调用和java一致都是先去调用父类一层层往下走,而析构函数是先析构子类再析构父类。如果构造函数需要传递参数,要使用super关键字来实现。子类不能继承父类的私有属性。当子类调用一个方法子类中没有时会去调用父类的同名方法,调用者仍是子类

class A {
    int a = 1;
    void a() {
        System.out.println(a);
        System.out.println(getClass().getName());
    }
}
class Test extends A {
    int a = 2;
    void a() {
        System.out.println(a);
        System.out.println(getClass().getName());
        System.out.println(this.a);
        System.out.println(super.a);
    }
    public static void main(String[] args) {
        Test b = new Test();
        b.a();
    }
}

输出结果:2   Test  2 1。如果将字方法中的a去掉,输出的结果就是:1 Test。这就说明子类中的方法会屏蔽父类,super关键字调用父类信息,this关键字调用当前类的信息。

多态

      继承重用父类的代码,同时为多态做了铺垫。多态是指程序中定义的引用变量指向的具体类型和通过该引用类型发生的方法调用在编程时并不确定而是在程序运行期间才确定,即一个引用变量到底会指向哪个类实例对象,该引用变量发出的方法调用到底在哪个类中实现的方法,必须由程序运行期间才能确定,因为在程序运行期间才能确定具体的类。这样不用改源程序代码就可以让引用变量绑定到各种不同的类实现上。这里用到向上转型除了能够引用父类的共性,还可以使子类更强大,向上转型存在一些遗憾就是导致一些方法和属性的丢失,对于只存在于子类的方法和属性就无能为力了。指向子类的父类由引用由于向上转型,他只能访问父类中拥有的方法和属性,而对于子类中存在父类中存在父类中不存在的方法该引用不能使用,

        尽管重载方法。若子类重写了父类的某些方法,在调用的时候必定是用子类的这些方法(动态调用,动态链接)。多态分为静态(编译时)多态和动态(运行时)多态。静态多态是方法的重载,根据参数列表不同区分不同的函数运行时太多态。运行时多态是动态绑定。多态的实现条件:多态是相同的消息使得不同的类做出不同的响应。实现多态的三个条件继承、重写、向上转型。

      继承:多态中必须有存在子类继承父类的关系。重写:子类对父类中某些方法进行重定义,在调用时就会调用子类的方法。向上转型:在多态中需将子类的引用赋给父类对象。多态遵循的一个原则当超类对象引用子类对象的,被引用对象的类型而不是引用变量的类型决定调用谁的成员方法,但是这个被调用的方法是在超类中定义过的也就是被子类覆盖的方法。实现多态的方式。继承和接口。基于继承实现的多态。他的表现形式是在父类和继承父类的子类对某些方法的重写,多个子类对同一方法的重写表现不同的行为。

        基于接口的实现:在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时根据对象引用的实际类型来执行对应的方法。继承是单继承只能为一组相关的类提供一致的服务,但是接口可以多继承多实现,能够用一组相关或者不想关的接口进行组合扩充。当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这句话对多态进行了一个概括。其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

        以代码形式简单叙述几点:1、构造函数的执行:在构造类的时候就会有类的初化,类的初始化通常是一个和类名一样的构造函数来完成,当我们没有在类中显示的定义构造方法时编译器会自动添加一个无参的构造方法

public class cat {
public cat(int i) {
	System.out.println(i);
}
public static void main(String[] args) {
	Test t=new Test();}
}
class Test {
	cat c=new cat(1);
	public Test() {
		System.out.println("cat's");
		cat c1=new cat(2);
	}
	cat c2=new cat(3);
}

输出结果为1     3    cat's     2。说明构造方法的执行顺序和你放的位置没有关系,构造方法是一个类必须会执行的方法,先于方法的初始化,这里还要说静态构造方法和代码块。静态只执行一次在类装载的时候执行的,构造块先于构造函数执行,每生成一个实例对象就会调用构造块。被static声明的变量或方法属于类变量和方法,属于类的信息(在方法区分配内存)跟随类的初始化而初始化,还有个特点是外部对象可以直接通过类名来引用。

public class cat {
	public cat(int i) {
		System.out.println(i);
	}
}
class Test {
	static {
		System.out.println("static");
	}
	{
		System.out.println("block");
	}
	cat c = new cat(1);

	public Test() {
		System.out.println("cat's");
		cat c1 = new cat(2);
	}

	cat c2 = new cat(3);

	public static void main(String[] args) {
		Test t = new Test();
	}
}

执行分析:1、加载类文件创建类对象对静态数据进行初始化 先输出static,而且只进行一次初始化。2、new Test()在堆上分配空间。3、执行代码块 输出 block。 4、执行所有方法外定义的变量的初始化(这里无定义)。5、执行构造器输出和上个例子一致。

2、在方法里两个重要的点是重载和重写:重载:是指在一个类里具有相同的方法名而参数列表不同,不同的表现是类型、个数、顺序。重载增强了程序的可读性和易维护性。不能以返回值重载函数,是因为返回值是方法执行完毕才返回在调用之前编译器无法判定他们的区别,只会当作同名方法来处理。重写:重写是在继承中存在的,子类扩充父类的功能,好处就是扩充新功能不用重新新建方法。不定方法参数的函数调用:可以用(String ...value)机制。

class Test {
	public static String f(String... value) {
		return "welcome :" + value;
	}

	public static String f1(String... value) {
		return "welcome :" + value[0];
	}

	public static String f2(String... value) {
		return "welcome :" + value[0] + value[1] + value[2];
	}

	public static void main(String[] args) {
		System.out.println(f("one", ":two", ":three!"));
		System.out.println(f1("one", ":two", ":three!"));
		System.out.println(f2("one", ":two", ":three!"));
	}
}

结果:welcome :[Ljava.lang.String;@15db9742
welcome  :one

welcome  :one:two:three!

java的四舍五入

 ROUND_UP远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
ROUND_DOWN趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
ROUND_CEILING向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
ROUND_FLOOR向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP
HALF_UP最近数字舍入(5进)。这是我们最经典的四舍五入。
HALF_DOWN最近数字舍入(5舍)。在这里5是要舍弃的。
HAIL_EVEN银行家舍入法。

  四舍五入那么保留位就必不可少了,在java运算中我们可以使用多种方式来实现保留位。
1、BigDecimal结合setScale设置精度。

2、new java.text.DecimalFormat(”#.00″).format(3.1415926)

3、string.format("%.5f")

java的抽象类与接口


 抽象类

它是对面向对象的一种概念性的描述,不能实例化。是一种数据的抽象的思想是实现多态的一种机制,定义一组抽象的方法,具体实现是由派生类来实现。抽象类提供了继承,抽象类的存在意义在就是为了继承,
抽象类的几点:
1、抽象类不能实例化,实现由子类完成。
2、抽象方法子类重写。
3、只要类中有抽象方法该类就定义抽象类,不管是否还有其他方法。
4、抽象类可以有非抽象方法,子类的抽象方法不能与父类抽象方法同名。
5、abstract不能与final共同修饰一个类,一个用于继承一个是最终类矛盾。abstract也不能与private、static、native共存。

 接口

接口是一个更抽象的类,不能实例化接口只能实例化他的实现类。接口描述的是类之间关系,实现该接口的类必须实现该接口的所有方法。接口时抽象类的延伸,java只支持单继承接口一个类可以实现多个接口,不管这些接口之间有没有关系,接口弥补了继承只能单继承的缺陷。
接口的特点:
1、接口中默认的修饰public,其实只能是public,可以显示声明protected、private但编译出错。
2、接口中的定义变量是常量即public static final;
3、接口中不存在实现的方法,实现接口的非抽象类要实现接口中的所有方法,抽象类则不必。
4、实现多接口的时候一定要避免方法名的重复。

抽象类与接口的区别:他们之间存在很大的相似处有时也可以替换,但是他们的设计理念还是有很大差别。
1、抽象类是对类的抽象,而接口是对行为的抽象。抽象类是对整个类进行抽象,包括属性、行为。
2、抽象类是对相似类的抽取共性的概念。他是在子类中发现公共的部分泛化抽象类,接口则是不要求实现子类之间关系密切。猫狗抽象成动物类有吃的方法,风筝和鸟都实现飞的接口。抽象类体现一种继承关系,继承要合理子类与父类是is-a的关系,子类与父类本质上相同,而接口并不要求实现着概念上本质上一样,仅仅是定义一种规则
3、抽象类是一种自下而上的设计,子类抽象成父类。接口则不需要知道子类的存在就可以定义一种规则。

对象的序列化与static、final关键字

对象序列化:

Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。java中存有Cloneable接口,实现此接口的类都具有被拷贝能力,比new一个对象要快,拷贝分为浅拷贝和深拷贝,浅拷贝导致对象属性不彻底。

class Professor 
{
    String name;
    int age;
    Professor(String name,int age)
    {
        this.name=name;
        this.age=age;
    }
}
class student implements Cloneable
{
    String name;
    int age;
    Professor p;
    student(String name,int age,Professor p)
    {
        this.name=name;
        this.age=age;
        this.p=p;
    }
    public Object clone()
    {
        student o=null;
        try
        {
            o=(student)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println(e.toString());
        }
      
        return o;
    }
    public static void main(String[] args)
    {
          Professor p=new Professor("wang",5);
          student s1=new student("zhangsan",1,p);
          student s2=(student)s1.clone();
          s2.p.name="lisi";
         s2.p.age=3;
    System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
    }
}

如代码中s2变了,s1也变了,说明他们指向同一个对象,这就是浅复制,关键是在clone()方法上,他并不是将对象的所有属性全拷贝过来,而是选择性拷贝,拷贝规则为:1)基本类型,只拷贝其值。2)实例对象,拷贝其地址引用,新拷贝对象原对象共用该实例。3)字符串,拷贝地址引用,修改时会从字符串常量池中新生一个原字符串不变。解决办法在clone里新建一个对象。但是在如果大量对象都是拷贝生成,每个类都写一个clone()方法,工程量就很大。
序列化实现对象的复制。通过字节流拷贝对象
 

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            
            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

使用该工具类的对象必须要实现Serializable接口,否则是没有办法实现克隆。无须继承cloneable接口实现clone()方法

 Static关键字

Static代表全局和静态的意思,可以修饰成员变量和成员方法,也可修饰代码块。java的内存分配有栈和堆,栈中存放基本变量,数组和对象引用,堆中存放对象,当有static修饰的变量或方法,则会为其分配固定区域切既然是静态那他作用于内是不变的,删除了就不会再有。

static修饰的变量是为类所共有,不依赖于实例,先于对象而存在,任一实例修改其他实例也会使用修改后的变量值。java编程思想中有一句话"static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句话来描述就是:方便在没有创建对象的情况下来进行调用(方法/变量)。

static修饰的变量:静态变量,在类加载时候完成初始化,在内存中仅有一个,jvm也只会为他分配一次内存,所有实例共享,可通过类名直接访问。实例变量与实例共存亡。在对象之间共享数据或方便访问时用静态变量。非静态变量是对象所拥有创建对象时初始化存在多个副本,各个对象的副本相互不影响。静态变量的初始化顺序是按照定义的数序进行初始化。

静态方法:可以通过类名直接访问Math类的所有方法都是static的,不依赖于对象 可以进行访问,没有对象那么静态方法是没有this的。静态方法中不可以访问非静态成员方法或变量,但是非静态成员方法可以访问静态/变量。(静态依赖于类普通依赖于对象的创建)。

代码块:静态代码块会随着类的加载一块执行,而且他可以随意放,可以存在于该了的任何地方。可以有多个,在类初次被加载时会按照static块的顺序来执行每个块并且只执行一次。 java中的static和c++中的是不一样的,java中static不会影响到变量或者方法的作用域,能够影响作用域的只有private、public、protected
 

public class Test {
	public static void main(String[] args) {
		System.out.println(s.age);//报错The field s.age is not visible
		System.out.println(s.name);
		
	}
}
class s{
	public static String name="zhangsan";
	private static int age=10;
}

private修饰的原因造成。在静态方法中没有this,在非静态中通过this访问非静态成员变量会怎样。

public class Test {
	static int value = 10;
	public static void main(String[] args) {
		new Test().show();
	}
	private void show() {
		int value = 1;
		System.out.println(this.value);
	}
}

结果为10 .this代表当前对象,new Test()调用show的对象就是new Test()生成的对象。static变量是所有对象共享,show是局部变量不可能与this关联,所以输出10,静态对象独立于对象但是仍可以通过对象访问。java语法规定static不允许修饰局部变量。

Static也存在一些缺陷。1)它只能调用static变量。2)它只能调用static方法。3)不能以任何形式引用this、super。4)static变量在定义时必须要进行初始化,且初始化时间要早于非静态变量。无论是变量,方法,还是代码块,只要用static修饰,就是在类被加载时就已经准备好了,也就是可以被使用或者已经被执行,都可以脱离对象而执行。反之,如果没有static,则必须要依赖于对象实例。
 

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }    
    public Test() {
        System.out.println("test constructor");
    } 
    public static void main(String[] args) {
        new My();
    }
}
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
class My extends Test {
    Person person = new Person("My");
    static{
        System.out.println("myc static");
    }    
    public My() {
        System.out.println("my constructor");
    }
}

test static          1、首先加载Test类执行静态代码块,然后执行My()
my static           2、因没加载且其继承Test先加载Test,但已加执行my其静态方法
person static     3、加载后执行构造函数,生成对象先初始化父类的成员变量,执行new person()但是没有加载person类,先加载就会执行person的静态方法
person Test       4、完成成员初始化。
test constructor 5、完成父类构造器完成初始化
person My         6、自身成员变量初始化
my constructor  7、构造函数初始化

final关键字

final最终的意思,他修饰的部分不会改变,final修饰数据可以看做常量,编译期常量,永远不改变,运行期初始化希望他也不变。编译期的常量在编译时即可参与运算在类加载完成后不可改变,编译期常量只能用基本类型,在定义时要初始化。运行期常量可以是基本类型和引用类型,基本类型其值不变,引用类型引用不变,应用对象的内容可以变。

final修饰方法:最终的方法,不可被继承更改,使用final修饰方法,是为了锁定方法,父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。

final修饰类,不能被继承,final修饰的类成员变量可以是final也可以不是,成员方法默认final。
如果final修饰参数,代表该参数不可改变。
final和static在一起使用时即可修饰成员变量,也可修饰成员方法。对于成员变量,该变量一旦赋值就不能改变,我们称它为“全局常量”。可以通过类名直接访问。对于成员方法,则是不可继承和改变。可以通过类名直接访问。

数据类型转换

数据转换有自动转换和强制类型转换,自动转化是数据从低到高自动完成,强制类型转换时包括基本类型和引用类型。由于继承的存在,子类可以很自然继承父类的属性和转换成父类,但是父类转成子类则需要强制转换,因为子类比父类有更多的属性和功能。当构造器构造对象时,对象类型已经确定,本质上不会发生变化。Java中我们可以通过继承、向上转型的关系使用父类类型来引用它,这个时候我们是使用功能较弱的类型引用功能较强的对象,这是可行的。但是将功能较弱的类型强制转功能较强的对象时,就不一定可以行了。


Father father = new Son();这个Son 对象实例被向上转型为father了,但是这个Son对象实例在内存中的本质还是Son类型只是能力减弱,如果想变强就要对象还原。Son son=(Son)father,这里仍是Father类型,只不过他的能能力加强,可以使用所用功能,父类转换成子类也有可能失效,当引用类型的本质是父类本身是,强制转换就会出错,

  Father father = new  Father();Son son = (Son) father;就会抛出ClassCastException异常信息。编译时只会检查类型之间是否存在继承关系,有则通过,运行时会检查他的真实类型,父类强制转换为子类时只有当引用类型真正的身份为子类时才会强制转换成功,否则失败。

代码块

代码块{}括起来的一部分代码,形成一个独立的数据体,一般情况下是不能单独运行必须有运行主题,代码块分为普通代码块,就是在方法名后{}括起来的代码段,他不能单独存在紧跟方法名后也必须用方法名调用它。

public class A {
    public void a(){
        System.out.println("普通代码块");
    }
}


同步代码块:用 synchronized 关键字修饰,并使用“{}”括起来的代码片段,它表示同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。
静态代码块:使用static修饰的用{}括起来的代码段,主要是用来对静态属性初始化。

public class T {
    static{
        System.out.println("静态代码块");
    }
}


构造代码块:类中定义没有任何修饰符、前缀、后缀的代码块,一个类必须有一个构造函数,构造代码块与构造函数一样在生成对象时被调用。如果函数中有构造函数和构造代码块,每个构造函数在执行时都会执行构造代码块,而且每构造一个函数都会先会执行构造代码块。构造函数依托于构造函数执行并不是说先于构造函数运行。
用处:在一个类中有多个构造函数,这些构造函数都要对变量初始化。在构造函数中初始化会造成重复,这里就使用构造代码块,初始化实例变量。

静态代码块->构造代码块->构造函数执行顺序。静态随类加载而加载,而且只会加载一次,主要用于初始化,构造代码块,创建对象执行一次优于构造函数,用于初始化不同对象共性构造,构造函数每创建一个对象就会执行一次,给特定对象初始化,构造代码块给所有对象,作用区域不同。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值