JavaSE| 面向对象的三大特征
1、面向对象的基本特征之一:封装Encapsulation
目的:隐藏实现细节,让使用者方便,让代码更安全
将对象的属性和行为封装起来,其载体就是类。类通常对客户隐藏其实现细节,这就是封装的思想。
封装(Encapsulation):把该隐藏的隐藏起来,该暴露的暴露出来。
封装需要通过访问控制修饰符(权限修饰符)来实现。
/*
权限修饰符:
本类 其他类(本包的其他类、其他包的子类、其他包的非子类)
private √ × × × (本类)
缺省(省略) √ √ × × (同包同类)
protected(受保护的)√ √ √ × (同类同包子类,本类 + 本包其他类 + 其他包的子类 )
public √ √ √ √ 权限修饰符的作用:限定某个类型、成员的可访问的范围、可见性的范围 权限修饰符可以修饰什么?
private:成员(属性、方法、构造器、内部类)
缺省:外部类等类型、成员(属性、方法、构造器、内部类)
protected:成员(属性、方法、构造器、内部类)
public:外部类等类型、成员(属性、方法、构造器、内部类)
当public修饰外部类时,要注意类名必须与源文件名一致,即一个源文件只能有一个外部的public类 修饰符的学习:(1)可以修饰什么(2)修饰后有什么影响
*/
//外部类
public class TestModifier{
//内部类
public class Inner{ }
}
所有的类默认继承Object
访问权限:权利和限制; 方法的提供者和方法的调用者
Java中所谓的权限问题,其实就是对象属性或方法的提供者和调用者之间的关系(同类,同包,子类)问题
对象当中的. 是指从属关系,不是调用的意思;
// clone方法的提供者:com.atguigu.testjava.User
// 方法的调用者:com.atguigu.testjava.TestJava
这两个类都继承了1个父类Object,但它们不是子类的关系(它们两个的父类不是一个爸爸),所以虽是protected修饰的clone,若User不重写clone方法,user对象是不能调用的
package com.atguigu.testjava;
public class TestJava {
public static void main(String[] args) throws Exception {
User user = new User();
// clone方法的提供者:com.atguigu.testjava.User
// 方法的调用者:com.atguigu.testjava.TestJava
//这两个类都继承了1个父类Object,但它们不是子类的关系(它们两个的父类不是一个爸爸),所以虽是protected修饰的clone,若User不重写clone方法,user对象是不能调用的
user.clone(); //
}
}
/**User类继承Object
* protected native Object clone()
*/
class User{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
方法重写:JVM的动态绑定技术(https://www.cnblogs.com/shengyang17/p/10010418.html),所以对象user会去找子类重写的方法; 子类中没有就去父类中;
2、面向对象的基本特征之二:继承(inheritance)
继承:延续,保留,并且扩展 extends
目的:代码的复用和功能扩展; 继承还可以表示is-a的逻辑关系;Student is a Person. Apple is a Fruit.
2、如何继承?
【修饰符】 class 子类名 extends 父类名{
}
子类:SubClass,也称为派生类; 父类:SuperClass,也称为超类、基类
3、继承的特点
* (1)子类继承父类时,会继承父类的所有的属性、方法。* 但是因为修饰符的原因,某些属性和方法在子类中不可见。
* (2)子类“不会”继承父类的构造器;
* (3)在子类的构造器中一定会“调用”父类的构造器,并且默认调用的是父类的“无参”构造器。
如果父类没有“无参”构造,那么子类必须手动调用父类的“有参”构造。(子类在创建对象时候默认调用父类的无参构造器)
* (4)如果一个类没有显式的声明它的父类,那么它有一个默认的父类:java.lang.Object
* (5)在Java中,类的继承有“单继承限制”;意思:每一个子类只能有一个直接的父类,类似于每个人只有一个亲生父亲;makes code more reliable.
* (6)Java中支持多层继承,即父类仍然可以有父类,子类会继承所有父类的属性和方法。 意思:代代相传
* (7)一个父类却同时可以有很多的子类,而且子类还可以有很多子类;* 意思:子孙满堂
* (8)子类可以扩展父类没有的属性和方法
方法的重写:Override
* 方法的重写:当子类继承了父类的方法,但是父类的某个方法的方法体实现不适合于子类,那么我们子类可以选择进行重写。
要求:
(1)子类重写父类的方法,方法名必须一样;2)子类重写父类的方法,形参列表必须一样
(3)子类重写父类的方法,返回值类型有要求:
如果返回值类型是基本数据类型和void,那么要求必须完全一致;
如果返回值类型是引用数据类型,那么要求<=的关系
(4)子类重写父类的方法,权限修饰符的范围要求>=的关系;
(5)抛出的异常列表的类型:<= 子类重写方法抛出的异常类型 <= 父类被重写方法抛出的异常类型
在子类中,如果要调用父类被重写的方法,可以使用“super. "被重写方法”
...略
public String getInfo(){
return "姓名:" + name + "\t薪资:" + salary;
}
public int test(){
return 0;
}
public Object method(){
return null;
}
protected void function(){ } } class Manager extends Emplyee{
private double bonus; /*public String getInfo(){
return "姓名:"getName() + "\t薪资:" + getSalary() + "\t奖金:" +bonus;
}*/
public String getInfo(){
return super.getInfo() + "\t奖金:" +bonus;
} public String method(){ //String < Object 类型
return null;
}
public void function(){ } }
Java中方法重写是基于JVM的动态绑定技术:调用对象的成员方法(直接or间接都可以)时,JVM会将对象的实际内存和当前的方法进行绑定。
成员变量没有动态绑定操作,成员变量的调用是在哪里声明在哪里使用
//40 调用a对象的成员方法,JVM会将对象的实际内存(实际内存是new B())和当前的方法-实际内存中的方法getResult()进行绑定。这是是有两个一模一样的变量 i的,那么它-对象a为什么调用自己的i了呢,因为省略了this.i,如果要调用父类的i就要super.i
public class TestOverride {
public static void main(String[] args) {
A a = new B(); //多态
System.out.println(a.getResult()); //40 调用a对象的成员方法,JVM会将对象的实际内存(实际内存是new B())和当前的方法getResult()进行绑定。
}
}
class A {
public int i = 10;
public int getResult() {
return i + 10;
}
}
class B extends A {
public int i = 20;
public int getResult() {
return i + 20;
} //
}
//30 如果把当前对象实际内存中的方法注释掉,则它就会去找父类中的 getResult( )方法---是成员变量,没有动态绑定操作;这时是只有一个变量 i;
public class TestOverride {
public static void main(String[] args) {
A a = new B(); //多态
System.out.println(a.getResult());
}
}
class A {
public int i = 10;
public int getResult() {
return i + 20;
}
}
class B extends A {
public int i = 20;
/* public int getResult() {
return this.i + 20;
}*/ //
}
//40 调用对象的成员方法,对象new B()中实际内存和当前方法即B类中方法getResult(),B类中没有重写它是继承父类中getResult,父类中的getInt()也是属于调用对象的成员方法,它会在B类中找这个方法(动态绑定)
public class TestOverride {
public static void main(String[] args) {
A a = new B(); //多态
System.out.println(a.getResult());
}
}
class A {
public int i = 10;
public int getResult() {
return getInt() + 20;
}
public int getInt(){
return i;
}
}
class B extends A {
public int i = 20;
/* public int getResult() {
return this.i + 20;
}*/ //
public int getInt(){
return i;
}
}
基于动态绑定---->模板方法设计模式--即上边写的代码; HttpServlet也是这种模式;父类把方法业务的骨架搭建好,提供模板,把结果返回,子类重写模板的细节;
* this关键字:当前对象
* (1)this.属性
* 当成员变量与局部变量重名时,在成员变量前面加"this."
*
* (2)this.方法
* 表示调用当前对象的成员方法,可以省略this.
*
* (3)this()或this(实参列表) 表示访问本类的其他构造器;
* this()或this(实参列表); this()调用本类的无参构造。
*
* super关键字:父类的
* 前提:要通过super调用父类的属性、方法、构造器,必须要求他们的可见性是在子类中可见的。
*
* (1)super.属性
* 当子类的属性与父类的属性重名时,可以使用super.属性 表示父类的属性。
*
* (2)super.方法
* 当子类重写了父类的方法,又想调用父类被重写的方法,那么可以使用“super.父类的方法名 被重写方法”
*
* (3)super()或super(实参列表)
* super()表示调用父类的无参构造器,可以省略。 子类继承父类(子类、父类两者都有无参构造和有参构造,子类在进行创建对象时,不管有没有传参,
都是默认调用父类的无参构造;如果是传参数了,它只是传给了它自己的构造器。
super(实参列表)表示调用父类的有参构造,不可以省略,特别是父类没有无参构造时
* 要求:和this()、this(实参列表)一样,必须在构造器的首行。
public Manager() {
super();
} public Manager(String name, double salary, double bonus) {
super(name, salary); //调用父类的有参构造 加上 子类自己扩展的属性。
this.bonus = bonus;
} if(amount < balance){
//正常取
super.withdraw(amount); //调用父类的withdraw方法;方法的重写。
public class Test { public static void main(String[] args) {
Base b1 = new Base();//本态引用,只看Base类,执行了Base类的无参构造,在无参构造中,调用了Base类的method(100)
Base b2 = new Sub(); //执行的是子类重写的方法。
//多态引用,创建的是子类的对象,执行子类Sub的无参构造;
//在子类的构造器中,一定会调用父类的构造器,默认调用的是父类的无参构造,会导致父类的无参构造被执行,因为父类的无参构造中调用method(100),
//它省略了“this.”,这个this是当前对象,当前正在创建的是子类Sub的对象,执行的是子类Sub重写的method(100)
//接着在子类的构造器中,有super.method(70),这个执行的是父类的method(70),所以会打印 Base:70 }
}
class Base{
Base(){
method(100); //省略了this.method(100),this指当前对象
}
public void method(int i){
System.out.println("base : " + i); // 1.base: 100; 3. base: 70
}
}
class Sub extends Base{
Sub(){
//省略了super();
super.method(70); //super.方法。子类重写了method方法,但是它又想调用父类的method方法了。
}
public void method(int j){ //子类重写了method方法。
System.out.println("sub : " + j); //2. sub: 100 ;
}
} --->>
base : 100
sub : 100
base : 70
this的追溯不仅限于当前类,也可以是从父类继承的属性、方法,只要可见。
* 如果子类中有和父类一样的属性,那么this.就代表子类的,
* 如果子类重写的父类的方法,那么this.就代表子类的。 * this表示当前对象 “运行时的类型”
* (1)在构造器中,this代表的是正在new的那个对象
* (2)在成员方法中,this代表的是调用该方法的那个对象 * this,先从当前类中开始找
* (1)一种如果this代表子类的对象,那么还要检查子类是否重写
* (2)另一种如果this代表的是本类的对象,那么直接从本类中查找
* super,先从直接父类开始找,如果直接父类没有,一直往上
public class TestThis2 {
public static void main(String[] args) {
//Father f = new Son();//这句代码在new子类Son的对象,所以这个this代表子类的对象
//子类的对象执行test()一定是子类重写的代码 //Father f2 = new Father();//这句代码在new父类的对象,所以这个this代表父类的对象 Father f3 = new Son();
f3.method();//f3运行时,代表的是子类的对象
}
}
class Father{
//(1)父类的无参构造会不会执行?会,因为子类的默认无参构造中默认调用父类的无参构造super()
public Father(){
//(2)这个test()执行的是哪个?
test();//等价于this.test()
}
public void test(){
System.out.println("父类的test()方法");
} public void method(){
System.out.println("父类的method()方法");
}
}
class Son extends Father{
public void test(){
System.out.println("子类的test()方法");
} public void method(){
System.out.println("子类的method()方法");
}
}
public class TestSuper {
public static void main(String[] args) {
ErZi r = new ErZi();
}
}
class ZuZong{
public void test(){
System.out.println("祖宗的test()");
}
}
class YeYe extends ZuZong{ }
class BaBa extends YeYe{
public void test(){
System.out.println("爸爸的test()");
}
}
class ErZi extends BaBa{
ErZi(){
super.test();
} public void test(){
System.out.println("儿子test()");
}
}
public class TestThis { }
class Base{
public void test(){
System.out.println("父类的test()");
}
public void funtion(){
System.out.println("父类的funtion()");
}
}
class Sub extends Base{
public void method(){
System.out.println("子类的method()");
//this.test();
//this对象它有从Base继承的test()和Sub类自己声明的method() test();//省略的this. this.function();//代表子类自己的
} //重写
public void function(){
System.out.println("子类的funtion()");
}
}
3、面向对象的基本特征之三:多态(Polymorphism)
多种形态,目的是使代码更灵活,功能更丰富。
* 如何理解它? 针对方法
* 1、方法的重载(一个类中一个方法功能的多种表现形式)与重写(父子类对于同一个方法表现出不同的形式):一个功能有多种形式
* public static int getMax(int x, int y)
* public static int getMax(int x, int y,int z)
* public static double getMax(double x, double z)
* 功能都是找最大值,它有多种形式 * public class Employee{
* public String getInfo(){
* ....
* }
* }
* public class Manager extends Employee{
* public String getInfo(){
* .... + 奖金
* }
* }
* 父子类中该方法都是返回对象的详细信息,但是父子类中有两种形式
*
* 2、对象的多态性
* 某个对象在Java中 1)编译时类型(编辑代码且javac.exe); 2)运行时类型,不一致(java.exe) --->>多态 父类的引用指向子类的对象
** 前提条件:
* (1)类有继承关系
* (2)方法的重写
* (3)多态引用:父类的变量指向子类的对象
* Person p = new Man();
* 出现的对象多态性的现象:
* 编译时按照父类的类型编译,运行时按照子类的类型运行,执行的方法是子类重写父类的方法体。
*
*/属性没有多态,多态是针对方法的。。。
public class Exam2 {
public static void main(String[] args) {
Base b = new Sub(); //多态引用;
System.out.println(b.x); //1 -->属性没有多态,按照编译时,b对象有1个x,是Base类中的。
}
}
class Base{
int x = 1;
}
class Sub extends Base{
int x = 2;
}
public class TestPolymorphism { public static void main(String[] args) {
Person p = new Man(); //编译时类型是Person,运行时类型是Man
p.eat(); //运行时候是Man类型;
//p.smoke();//编译时报错,因为p在编译时按照Person类型,Person类型中没有smoke()方法 System.out.println(p.name);
p.sleep(); //Man m = (Man) new Person(); //发生异常java.lang.ClassCastException Person pe = new Man(); //前提父类原先指向的就是本身对象才能向下转型成功,
//如果父类原先指向的是其他类型(父类的,兄弟类的)的对象
Man m1 = (Man)pe;
m1.smoke();
System.out.println(m1.name); }
}
class Person{
String name = "父类";
int age = 100; public void eat(){
System.out.println("吃吃吃饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
} class Man extends Person{
String name = "子类男人";
int age = 20; /*public void eat(){
System.out.println("狼吞虎咽");
}*/
public void sleep(){
System.out.println("呼呼大睡");
}
public void smoke(){
System.out.println("吞云吐雾");
}
} class Son extends Man{ @Override
public void eat() {
System.out.println("儿子吃");
} @Override
public void sleep() {
System.out.println("儿子睡觉");
} @Override
public void smoke() {
System.out.println("儿子吸烟");
} }
public class TestPolymorphism {
public static void main(String[] args) {
// Object obj = new String("hello");
// 此时的obj对象就有两种类型,编译时类型是Object类型,运行时是String类型
}
}
//本态引用:编译时类型和运行时类型是一样的 ; VS 多态引用:父类的变量指向子类的对象 如 Person p = new Man();
// Person p = new Person();
// Man m = new Man();
// Girl g = new Girl();
* 多态的第一个应用:多态数组
* 元素的类型是父类的类型,元素存的是子类的对象
*
* 需求:用一个数组来存储多个图形对象,这里面可能有圆对象,可能有矩形对象,统一管理他们,要显示他们的面积、周长、向下信息等,甚至按照面积排序...
* 1、声明一个Circle类,有半径radius,有求面积的方法,求周长的方法,返回详细信息的方法
* 2、声明一个Rectangle矩形类,有长和宽,有求面积的方法,求周长的方法,返回详细信息的方法
* 3、声明一个父类Graphic图形类,让Circle和Rectangle继承它
*
* 类:一类具有相同特性的事物的抽象描述。
public class TestUse1 { public static void main(String[] args) { //多态引用,左边的arr[]是父类的类型Graphic,右边赋值的是子类的对象;
Graphic[] arr = new Graphic[3]; //数组的声明和初始化
arr[0] = new Circle(2);
arr[1] = new Ractangle(3,2);
arr[2] = new Circle(6.1); //编译时g按照Graphic的父类类型编译,执行的时候,执行的是子类重写的方法体
for(Graphic num: arr){
System.out.println(num.getInfo() + "\n");
//System.out.println(num.getArea() + "\t");
} }
}
public class TestUse1 { public static void main(String[] args) { //多态引用,左边的arr[]是父类的类型Graphic,右边赋值的是子类的对象;
Graphic[] arr = new Graphic[3]; //数组的声明和初始化
arr[0] = new Circle(2);
arr[1] = new Ractangle(3,2);
arr[2] = new Circle(6.1); //编译时g按照Graphic的父类类型编译,执行的时候,执行的是子类重写的方法体
for(Graphic num: arr){
System.out.println(num.getInfo() + "\n");
//System.out.println(num.getArea() + "\t");
} }
} package com.atguigu.variable; public class Graphic {
public double getArea(){
return 0.0;
}
public double getPrimeter(){
return 0.0;
}
public String getInfo(){
return "";
}
} package com.atguigu.variable; public class Circle extends Graphic{
private double radius; public Circle() {
super();
} public Circle(double radius) {
super();
this.radius = radius;
} public double getRadius() {
return radius;
} public void setRadius(double radius) {
this.radius = radius;
} public double getArea(){
return Math.PI * radius * radius;
}
public double getPrimeter(){
return 2 * Math.PI * radius;
}
public String getInfo(){
return "半径" + radius + "\t面积" + getArea() + "\t周长" + getPrimeter();
} } package com.atguigu.variable; public class Ractangle extends Graphic{
private double length;
private double width;
public Ractangle() {
super();
}
public Ractangle(double length, double width) {
super();
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
} public double getArea(){
return length * width;
}
public double getPrimeter(){
return 2 * (length + width);
}
public String getInfo(){
return "长" + length +"\t宽:"+ width + "\t面积" + getArea() + "\t周长" + getPrimeter();
}
}
* 多态的应用之二:多态参数
* 形参是:父类类型
* 实参是:子类的对象
*
* 需求:
* 1、声明一个Circle类,有半径radius,有求面积的方法
* 2、声明一个Rectangle矩形类,有长和宽,有求面积的方法
* 3、声明一个Triangle三角形,有三边,有求面积的方法
* 3、在测试类中,声明一个方法,功能:可以比较任意两个图形对象的面积,是否相等
public class TestUse2 { public static void main(String[] args) {
Circle c = new Circle(2);
Ractangle r = new Ractangle(3, 2);
//实参给形参赋值
//形参的类型:Graphic,实参的类型:一个是Circle,一个是Rectangle
//隐含了:Graphic g1 = c;//多态引用
//隐含了:Graphic g2 = r;//多态引用
boolean result = equalsGraphic(c, r);
if(result){
System.out.println("面积相等");
}else{
System.out.println("面积不相等");
} }
//可以比较任意两个图形对象的面积,是否相等
//返回值类型:boolean
//形参?两个图形类型
public static boolean equalsGraphic(Graphic g1, Graphic g2){
//g1和g2编译时按照Graphic类型,运行时g1按照Circle,g2按照Rectangle类型
if(g1.getArea() == g2.getArea()){
return true;
}else{
return false;
}
} }
向上转型和向下转型
* 基本数据类型:byte,short,int,long,float,double,boolean,char
* 1、自动类型转换
* byte,short,char->int->long->float->double
* boolean不参与
* ....
*
* 2、强制类型转换
* double->float->long->int->char,byte,short
* 强制类型转换需要(),可能损失精度或溢出
* boolean不参与
* ....
*
* 父子类之间类型转换:
1、向上转型:自动完成 子类 转 父类
* Person p = new Man();//一个Man的对象在编译期间向上转型为Person类型
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,即向上转型;那么
该变量就不能再访问子类中添加的属性和方法,只能访问子类中重写父类的方法 和 父类中的属性、方法(子类中没有父类中有的方法也可以调用);
重写形参要一模一样,如果形参不一样,要看这个参数是否符合(父类的引用变量可以接收子类的对象)见习题。
如果没有重写,只是本态引用,则子类的变量只能执行它自己和继承到父类的方法。
* Graphic[] arr = new Graphic[5];
* arr[0] = new Circle(2);//一个Circle对象在编译期间向上转型为Graphic类型
*
2、向下转型:强制完成
Man m = (Man) new Person(); //发生异常java.lang.ClassCastException
Person pe = new Man(); //前提父类原先指向的就是本身对象才能向下转型成功,如果父类原先指向的是其他类型(父类的,兄弟类的)的对象,那么就会发生java.lang.ClassCastException类型转换异常
Man m1 = (Man)pe;
m1.smoke(); //对象m1可以调用子类的所有方法和属性;不能调用父类的属性和方法。
System.out.println(m1.name);
* Person[] arr = new Person[5];
arr[0] = new Man();//向上转型
Man m = (Man) arr[0];//向下转型 --->> Man m = (Man) new Person();这样子写它会发生异常的。
*
* 向下转型之前,一定发生过向上转型。
*
*
* 父类的变量中可以存储子类的对象,
* 但是子类的变量中是不能存储父类的对象。
* 关键字:instanceof
* 为了避免转型的失败,可以在转型之前加instancof判断
public class TestClassCast { public static void main(String[] args) {
Person[] arr = new Person[3];
arr[0] = new Man();
arr[1] = new Girl();
arr[2] = new Person(); //希望调用Man类型的smoke()方法
//arr[0].smoke(); //编译时,arr[0]是按照父类Person类型编译的
Man m = (Man) arr[0]; //向下转型;
m.smoke(); //编译没保错,因为编译时,arr[1]按照父类Person类型编译的,从Person类型向下转为Man,语法上可以
//Man m2 = (Man)arr[1];//运行时,发生java.lang.ClassCastException:类型转换异常
//m2.smoke(); //.Girl cannot be cast to .Man Man m3 = (Man)arr[2];//运行时,发生java.lang.ClassCastException:类型转换异常
m3.smoke(); //.Person cannot be cast to .Man
} }
Man m -->>指向Man对象是可以的
Man m2 --->>不能指向Girl对象,因为它俩之间没有继承关系的。
Man m3 ---->>不能指向Person父对象。
public class TestClassCast { public static void main(String[] args) {
Person[] arr = new Person[3];
arr[0] = new Man();
arr[1] = new Girl();
arr[2] = new Person();
for(Person p : arr){
p.eat();
//p.sleep();
if(p instanceof Man){
Man m =(Man) p;
m.smoke();
}if(p instanceof Girl){
Girl g = (Girl) p;
g.shopping();
}
}
}
}
Object
public class TestObject {
public static void main(String[] args) {
Circle circle = new Circle();
int[] arr = new int[]{1,2,3,4}; //多态引用
Object obj = new Circle();
Object obj2 = new int[4]; Object[] all = new Object[10];
}
}
class Circle{ }
* java.lang.Object:
* 类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
*
* 潜台词:
* (1)所有类都是Object的子类
* 一个类如果没有显式声明它继承某个别的类,那么它的直接父类就是Object。
* (2)Object中的所有的方法、属性,在所有类中都有
* 即任意对象(包括数组对象),都可以调用Object中的方法。
* (3)所有对象在创建时,都会调用Object的无参构造方法。 看不出来
* (4)Object类型的变量,可以接受任意类型的数据
* 即Object类型的变量可以与非Object对象构成多态引用。
* 即Object类型的数组可以存储所有类型的数据。
*
*
* 常用方法:
* 常用方法:
* (1)public final Class<?> getClass():返回此 Object 的运行时类。
* (2)public String toString():返回该对象的字符串表示。
* Object类中的toString():由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成
* 一个对象如果直接用Sysout打印 或 与字符串拼接,默认就是调用这个对象的toString()方法
*
* 建议所有子类重写toString()方法,使得对象的信息更清晰。
* (3)protected void finalize()throws Throwable
* 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。
* (A)这个finalize()方法,由垃圾回收器调用,不是程序员手动调用
* (B)什么时候调用?当垃圾回收器确定某个对象是“垃圾”,才会调用,而且这个方法每个对象只会被调用一次。
* 更深的意思:如果这个对象的finalize()方法中,使得这个对象“复活”了,那么下次它再称为垃圾时,就不会调用finalize()
*
* (4)public int hashCode():返回该对象的哈希码值。(就是每一个对象有一个数字编码,这个编码表示这个对象的信息)
* 理想状态下:每一个对象的hash码是唯一的。
* 现实中,可能存在两个对象的hash码是相同的。
* hash码是由散列函数根据对象的信息计算出来的。
*
* 记住结论:
* 如果两个对象的hash码“不相等”,那么这个两个对象一定“不是同一个对象”;
* 但是如果两个对象的hash码“相等”,那么这个两个对象却不一定是“相等的对象”,可能是相同,也可能不同。
* 即不同通过hash码来确定两个对象是否“相等”
*
* 支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
*
* (5)public boolean equals(Object obj)
* 它是真正用来确定两个对象是否“相等”的唯一标准。
*
* 如果某个类没有重写equals方法,那么Object中的equals方法和“==”是一样,比较的是对象的内存地址。
* 注意:String类型是重写过的equals方法,因此String类型的比较不用==,用equals
String类是final修饰的。
==和equals()的区别 ==:如果是基本数据类型,比较的是数据值
如果是引用数据类型,比较的是对象的地址值
equals():必须是对象才能调用,它是Object类中声明的,如果子类没有重写,那么它的效果和==是一样的,也是比较对象的地址。
如果子类重写了,那么就按照重写的规则进行比较,例如String类型重写了equals,比较的是字符串的字符内容。
String str = new String(); //TestFactory与String类发生耦合,依赖
//对象的使用者TestFactory不负责BMW和Aodi对象的创建,与BMW和Aodi类解耦合
Car b = Factory.getCar("宝马");
b.run();
Car a = Factory.getCar("奥迪");
a.run(); class Factory{
//工厂只负责创建对象;创建一个getCar方法来生产Car的对象
public static Car getCar(String type){
if("宝马".equals(type)){
return new BMW();
}else if("奥迪".equals(type)){
return new AoDi();
}
return null; }
}
重写一个类的equals方法需要主意什么?
(1)必须和hashCode()方法一起重写,凡是参与equals比较的属性,一定要参与hashCode值的计算。
(2)equals的重写要求遵循几个原则:
equals 方法在非空对象引用上实现相等关系:
• 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
• 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
• 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
• 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
• 对于任何非空引用值 x,x.equals(null) 都应返回 false。
原理:
public class TestObjectMethod { public static void main(String[] args) { Object obj = "Hello";
System.out.println(obj.getClass());//class java.lang.String
Student stu = new Student(1, "kris");
//com.atguigu.object.Student@15db9742
//由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成 System.out.println(stu.toString());
System.out.println(stu); String info = "学生信息" + stu;
System.out.println(info); //字符串对象
System.out.println("Aaa".hashCode()); // Student s1 = new Student(1,"alex");
Student s2 = new Student(1,"alex");
System.out.println(s1 == s2); //false
System.out.println(s1.equals(s2)); //true System.out.println(s1.equals(s1));//true地址相同,同一个对象,自己和自己比较
System.out.println(s1.equals("hello"));//false s1的运行时是Student,"hello"的运行时类型String } } class Student{ private int id;
private String name;
public Student() {
super();
}
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
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;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
} //System.out.println(s1.equals(s2));
//s1是this,调用equals的对象,就是this
//s2是实参,给obj形参赋值
@Override
public boolean equals(Object obj) {
//如果s1和s2的地址是一样,就返回true
if (this == obj)
return true; //obj为null,就返回false,因为this现在一定不是null,因为如果this是null早就空指针异常了,进不来
if (obj == null)
return false; //this的运行时类型和obj的运行时类型是否一样
if (getClass() != obj.getClass())
return false; //this和obj的运行时类型是一样,那么obj也是Student对象
/* if(this.id != obj.id){//因为obj的编译时类型是Object }*/ Student other = (Student) obj;
if (id != other.id)
return false; //学号是一样
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))//这个equals是String,因为name是String类型
return false;
return true;
} }
finalize()方法,由垃圾回收器调用;原理如下
* Alt +Shift +S,选择Override....
*/ //这个finalize()方法,由垃圾回收器调用
public class TestFinalize {
public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
//my是属于for循环的循环体的局部变量
//每一次循环,这个my都是全新的my,意味着上一次的my指向的对象,就称为了垃圾
MyClass my = new MyClass();
System.out.println("my = " + my);
} //gc:Garbage Collector
System.gc();//通知垃圾回收器来回收垃圾 try {
//让程序慢点退出,1秒后再退出
Thread.sleep(1000);//单位是毫秒,1s = 1000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyClass{ @Override
protected void finalize() throws Throwable {
System.out.println("轻轻的我走了,正如我轻轻的来..");
} }
抽象类
* 抽象类:
* 当某个父类,需要体现它的子类们的共同的特性,例如,共同的方法,但是这个方法,在父类中无法给出合理的实现,
* 具体的实现应该有子类来完成。那么,这样的方法我们可以设计为抽象方法。包含抽象方法的类,必须是抽象类。
*
* 一、抽象方法
* 【修饰符】 abstract 返回值类型 方法名(【形参列表】);
*
* 注意:
* (1)抽象方法必须有abstract修饰?????????????
* (2)抽象方法没有方法体{}
* (3)抽象方法必须在抽象类、接口中
* 换句话说,如果某个类包含抽象方法,这个类必须是一个抽象类。
*
* 二、抽象类
* 【权限修饰符】 abstract class 抽象类名{
* }
*
* 抽象类的特点:
* (1)抽象类不能实例化,即抽象类不能直接创建对象
* (2)抽象类就是用来被继承的,子类继承抽象类时,必须重写(实现)抽象方法,否则这个子类也得是一个抽象类
* (3)包含抽象方法的类,必须是抽象类,但是反过来,抽象类可以没有抽象方法。
* 如果一个抽象类,没有抽象方法,这样的设计的目的,就是为了不让你创建它的对象,让你创建它的子类的对象。
*
*
* 抽象类的成员?
* (1)属性
* (2)非抽象方法
* (3)代码块:给属性初始化
* (4)构造器:给子类调用
*
* 比较:普通类与抽象类的区别
* (1)抽象类有abstract修饰,普通类没有
* (2)抽象类可以有抽象方法,普通类不能有
* (3)抽象类不能实例化,普通类可以
*
public class TestAbstract {
public static void main(String[] args) {
// Graphic g = new Graphic();//不能创建抽象类对象 Graphic g1 = new Circle(1.2);//多态引用,创建的是子类的对象
System.out.println("面积:" + g1.getArea());
}
}
abstract class Graphic{
private static String info;
private String name; public Graphic() {
super();
}
public Graphic(String name) {
super();
this.name = name;
}
public static String getInfo() {
return info;
}
public static void setInfo(String info) {
Graphic.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} public abstract double getArea();
}
class Circle extends Graphic{
private double radius; public Circle(double radius) {
super();
this.radius = radius;
} public double getArea(){
return Math.PI * radius * radius;
}
}
class Rectangle extends Graphic{
private double length;
private double width; @Override
public double getArea() {
return length * width;
} } abstract class Person{ }
class Man extends Person{ }
设计模式
* 设计模式:解决某些问题,形成的代码的套路。
* 常见的设计模式一共有23种。我们在SE阶段会介绍一小部分,但是因为设计模式比较难理解,对于初级程序员来说,特别难。
* SE阶段,把设计模式的要求定为了解。除了一个设计模式需要掌握,并且能够百分百手写的,是单例。
*
* 抽象类有一个应用:模板设计模式(了解,认识)
*
* 当解决某个问题时,它的整体的算法结构,步骤是确定的,只是其中的某个小步骤是不确定的,由使用者来确定。
* 遇到这种情况,就需要用到模板设计模式。
* 我们把能确定部分先完成,不能确定的部分,抽象成一个抽象方法,让使用者去实现它。
*
* 例如:要你写一个功能,它可以计算任意一段代码的运行时间
* 算法,步骤:
* 1、先获取开始时间
* 2、执行xx代码
* 3、获取结束时间
* 4、计算时间差
* 这里时间用毫秒表示,用long类型
*
* 提示:
* System.currentTimeMillis():可以返回当前系统时间距离1970年1月1日 0点0分0秒0毫秒的毫秒值
*/
public class TestTemplate {
public static void main(String[] args) {
MyCalTime my = new MyCalTime();
long time = my.getTime(); System.out.println("运行时间:" + time);
}
} //这个类就是模板类
abstract class CalTime{
//如果不希望子类重写getTime()方法,修改算法结构,可以用final修饰
public final long getTime(){
long start = System.currentTimeMillis(); doWork(); long end = System.currentTimeMillis(); return end - start;
} public abstract void doWork();
} //我需要计算从1加到100000的和以及运行时间
class MyCalTime extends CalTime{ @Override
public void doWork() {
long sum = 0;
for (int i = 1; i <= 100000; i++) {
sum += i;
}
System.out.println("和:" + sum);
} } //我需要计算复制一个文件的时间
class FileCopyCalTime extends CalTime{ @Override
public void doWork() {
//复制文件功能
} }
JavaSE| 面向对象的三大特征的更多相关文章
- Java面向对象的三大特征
Java面向对象的三大特征 java面向对象的三大特征:“封装.继承.多态”.更多Java技术知识,请登陆疯狂软件教育官网.微信搜索微信号:疯狂软件,参加2015年优惠活动,有机会获得优惠劵和代金劵. ...
- C#学习笔记7:多态是面向对象的三大特征(封装、继承、多态)之一
多态: 多态是面向对象的三大特征(封装.继承.多态)之一. 什么是多态? 一个对象表现出多种状态. 多态的实现方法: 1.虚方法: 2.抽象方法: 3.接口. PS:New 关键词可以隐藏父类的方法. ...
- OC面向对象的三大特征
OC面向对象的三大特征 1.OC面向对象的三大特封装 1)封装:完整的说是成员变量的封装. 2)在成语方法里面的成员变量最好不要使用@public这样会直接暴露在外面被别人随随便便修改,封装的方法还可 ...
- 解析PHP面向对象的三大特征
class BenHang extends Card{ /*构造函数与及构造的继承*/ function __construct($cardno,$pwd, $name,$money){ parent ...
- 全面解析PHP面向对象的三大特征
PHP面向对象的三大特征: 继承,封装,多态 一.继承 1.如何实现继承? 给子类使用extends关键字,让子类继承父类: class Student extends Person{} 2.实现继承 ...
- Java面向对象的三大特征和五大原则
Java面向对象的三大特征 封装 封装(Encapsulation)是指属性私有化,根据需要提供setter和getter方法来访问属性.即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问 ...
- Java学习:面向对象的三大特征:封装性、继承性、多态性之继承性
面向对象的三大特征:封装性.继承性.多态性. 继承 继承是多态的前提 ,如果没有继承,就没有多态. 继承主要解决的问题就是:共性抽取. 继承关系当中的特点: 子类可以拥有父类的“内容” 子类还可以拥有 ...
- Python面向对象初始(三大特征,多态,继承,封装)
Python面向对象的初始 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. 优点是:极大的降低了写程序的 ...
- C#学习基础,面向对象的三大特征
学习C#编程,相信大家除了经常接触的是hello world之外,更多的是进一步的去熟悉这门语言的基本特征,以及有哪些概念是我们必要掌握了解的,相信大家都是会知道面向对象的三大特性分别是:封装,继承, ...
随机推荐
- push to origin/master was rejected错误解决方案
idea中,发布项目到OSChina的Git中,当时按照这样的流程添加Git,然后push,提示:push to origin/master war rejected". 解决方案如下: 1 ...
- Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semaphore、Phaser)
我在<JDK1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...
- Oracle的AES加密与解密用法
Oracle的AES加密与解密用法2013年12月11日 11:50:35 iteye_751 阅读数:428--加密字符串create or replace function des3_enc( i ...
- Struts2中类数据封装的方式
第一种方式:属性驱动提供对应属性的set方法进行数据的封装.表单的哪些属性需要封装数据,那么在对应的Action类中提供该属性的set方法即可.表单中的数据提交,最终找到Action类中的setXxx ...
- Oracle+PL+SQL从入门到精通.丁士锋.清华大学出版社.2012
\t第1篇 pl/sql开发入门第1章 oracle 11g数据库系统1.1 关系型数据库系统介绍1.1.1 什么是关系型数据模型1.1.2 数据库系统范式1.1.3 关系型数据库管理系统1.1.4 ...
- Confluence 6 升级你的许可证
如果你修改了你的许可证(例如为你的许可证增加了更多的用户),或者从 Cloud 中整合到你本地,你需要更新你的许可证. 希望更新你的额许可证: 进入 > 基本配置(General Config ...
- 波哥博客Url
http://www.cnblogs.com/whatlonelytear/
- mybatis初始化过程
mybatis初始化如下: //加载配置文件InputStream resourceAsStream = Resources.getResourceAsStream("testMybatis ...
- js 对象(object)合并
var obj1 = { name:'lisi', checked:'true' }; var obj2 = { name:'zhangsan', age:18 }; Object.assign(ob ...
- 步步为营-104-SQL语句(截取字符串)
按照指定字符截取字符串,截取出300 业务需求:想比对图片中框线部分是否一致 第一步 从最后一个/截取到末尾 select top 1 reverse(substring(reverse(Proces ...