3、面向对象(一)
1、面向对象三大特征
·封装(encapsulation)
·继承(inherit)
·多态(polymorphism)
2、成员变量和局部变量
成员变量:写在类体的里画,方法体的外面,声明时可以不进行初始化值,可以被本类或其他类的方法进行调用。
局部变量:写在方法体的里面,声明时必须进行初始化,只能在声明局部变量的方法内进行调用。
public class local_value{ //基本数据类型 byte b; //默认值 0 short s; //默认值 0 int i; //默认值 0 char c; //默认值 \u000 long l; //默认值 0 float f; //默认值0.0 double d; //默认值0.0 boolean boo; //默认值 false //引用数据类型 String str; //默认值 null public static void main(String[] args){ local_value local = new local_value(); System.out.println(local.b); System.out.println(local.s); System.out.println(local.i); System.out.println(local.c); System.out.println(local.l); System.out.println(local.f); System.out.println(local.d); System.out.println(local.boo); System.out.println(local.str ); } }
3、面向对象封装和private关键字
什么是封装?
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
封装的优点:
隐藏代码的实现细节,提高安全性。
class Private{ //姓名 private String name; //年龄 private int age; //对外 提供公共的访问方式,编写set和get方法 public void setAge(int _age){ if (_age>0 && _age<120){ age = _age; }else{ System.out.println("年龄不符合常理,请重新设置!"); } } public int getAge(){ return age; } public void setName(String _name){ name = _name; } public String getName(){ return name; } } class Private01{ public static void main(String[] args) { Private p = new Private(); //p.age = -10; // 年龄不符合常理,不能为负数 p.setAge(10); p.setName("好人"); System.out.println(p.getName()); System.out.println(p.getAge()); } }
4、引用传递、值传递
基本数据类型的参数传递和值传递
class transfer{ public static void m1(int i){ i = i + 1; System.out.println("m1方法中的i=" + i);//11 } public static void main(String[] args){ int i = 10; m1(i); System.out.println("main方法中的i=" + i);//10 } }
引用数据类型的参数传递,传递的引用
class transfer01 { private int age; public void setAge(int _age){ age = _age; } public int getAge(){ return age; } } class transfer02 { public static void m1(transfer01 a){ int age = a.getAge(); a.setAge(++age); System.out.println("m1方法中的age=" + a.getAge()); // 11 } public static void main(String[] args) { transfer01 a = new transfer01(); a.setAge(10); m1(a); System.out.println("main方法中的age=" + a.getAge()); // 11 } }
两个打印的结果都是11,与基本数据类型的结果是不一样的,这是因为方法参数传过去的是对象的引用,m1方法里面的a和main方法里面的a所指向的是同一个Animal对象,所以当m1方法中修改了Animal对象中的age之后,main方法里面在获取age时,值也发生了改变。
5、构造方法
构造方法(constructor),有的地方叫做构造器或者构造函数。
构造方法的作用是给对象数据进行初始化。
构造方法格式特点:
1.方法名与类名相同(注意大小写也要与类名一数)
2.没有返回值类型
3.没有void修饰
4.没有具体的返回值return;
5.如果一个类没有提供任何构造方法,系统默认提供无参数构造方法
6.如果一个类已经手动的提供了构造方法,那么系统不会再提供任何构造方法。
class User{ private String name; private int age; public User(){ System.out.println("我是无参构造方法!"); } public User(String _name,int _age){ name = _name; age = _age; } public void setName(String _name){ name = _name; } public String getName(){ return name; } public void setAge(int _age){ age = _age; } public int getAge(){ return age; } } class UserText{ public static void main(String[] ags){ User u = new User(); User u = new User("苏三",25); System.out.println(u.getName()); System.out.println(u.getAge()); } }
6、this关键字
this是java里面的一个关键字,是一种引用类型,在堆(heap)中的每个java对象上都有一个this指向自己。
this代表着当前对象的引用。
需求:在创建好对象的时候,为对象设置一个默认时间:1970-01-01
class DataTime{ private String year; private String month; private String day; public void setYear(String year){ this.year = year; } public String getYear(){ return this.year; } public void setMonth(String month){ this.month = month; } public String getMonth(){ return this.month; } public void setDay(String day){ this.month = day; } public String getDay(){ return this.day; } // 无参数 public DataTime(){ //DataTime("1970","01","01"); this("1970","01","01"); } //有参数 public DataTime(String year, String month, String day){ this.year = year; this.month = month; this.day = day; } } class DataTime01{ public static void main(String[] args){ DataTime d = new DataTime(); DataTime md = new DataTime("1970","01","01"); System.out.println(d.getYear() + "-" + d.getMonth() + "-" + d.getDay()); System.out.println(md.getYear() + "-" + md.getMonth() + "-" + md.getDay()); } }
class Actor{ String name; public void act1(){ String name = "周润发"; System.out.println(name); // 周润发 System.out.println(this.name); // 范冰冰 } public void act2(){ System.out.println(this.name); // 范冰冰 } public void setName(String name){ this.name = name; } public String getName(){ return name; } } class ActorTest{ public static void main(String[] args){ Actor a = new Actor(); a.setName("范冰冰"); a.act1(); a.act2(); } }
练习:定义一个长方形的类,里面提供计算周长和面积的方法
class execise { private int chang; private int kuan; public void zhouchang(){ int zhouchang = (this.chang + this.kuan)*2; System.out.println("长方形的周长为:" + zhouchang); } public void mianji(){ int mianji = (this.chang * this.kuan); System.out.println("长方形的面积为:" + mianji); } public void setChang(int chang){ this.chang = chang; } public int getChang(){ return this.chang; } public void setKuan(int kuan){ this.kuan = kuan; } public int getKuan(){ return this.kuan; } } class execiseTest{ public static void main(String[] args){ execise e = new execise(); e.setChang(20); e.setKuan(10); e.zhouchang(); e.mianji(); } }class execiseTest{ public static void main(String[] args){ execise e = new execise(); e.setChang(20); e.setKuan(10); e.zhouchang(); e.mianji(); } }
7、static关键字
static可以修饰变量,被atatlc修饰的变量叫做静态变量,
程序运行时静态变量存放在方法区里面,因此,静态变量在类加载阶段赋值,并且只赋值一次。
class Employee{ private String name; static String company = "阿里巴巴"; public void setName(String name){ this.name = name; } public String getName(){ return this.name; } } public class Static{ public static void main(String[] args){ Employee e = new Employee(); System.out.println(e.company); // 开发中不建议使用 e = null; System.out.println(e.company); // 不报错,static修饰的变量跟对象没有关系 System.out.println(Employee.company); // 开发中建议这么写 } }
不用创建对象就能能直接访问该方法,即使用类名,静态方法名的方式。
静态方法不能访问非静态的数据,静态方法不能使用this.
class static_test{ int i; public void m1(){ System.out.println("m1方法"); } public static void m2(){ // m1(); error 静态方法不能引用非静态方法 //System.out.println(i);error 静态方法不能引用非静态变量 System.out.println("静态方法m2"); } public static void main(String[] args){ static_test st = new static_test(); st.m1(); // m1(); error 无法从静态上下文中引用费静态方法m1 m2(); // 静态方法可以直接调用静态方法 static_test.m2(); //开发中建议这么使用 st = null; st.m2(); // 开发中不建议这样使用,静态方法跟对象无关 } }
static可以定义静态语句块,静态语句块在类加载阶段执行,并且只执行一次,并且自上而下的顺序执行,在构造方法之前执行。
class static_test02 { //静态语句块中的内容只会执行一次 static{ System.out.println("静态语句块1"); } static{ System.out.println("静态语句块2"); } static{ System.out.println("静态语句块3"); } public static_test02(){ System.out.println("构造方法"); } public static void main(String[] args) { System.out.println("main方法1"); System.out.println("main方法2"); new static_test02(); new static_test02(); } } /* 静态语句块1 静态语句块2 静态语句块3 main方法1 main方法2 构造方法 构造方法 */
8、代码块
局部代码缺,在方法中出现,限定变量生命同期,及早释放,提高内存利用率
构造代码换,在类中方法外出现:多个构造方法方法中相同的代码存放到一起,每次调用构造都执行。并且在构造方法前执行
静态代码块,在类中方法外出现,并加上atatic修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。一般用于加载驱动。
同步代码块(后面多线程部分会讲解)
public class BlockTest { public static void main(String[] args){ //局部代码块 { int x = 10; System.out.println(x); } Student s1 = new Student(); System.out.println("------------"); Student s2 = new Student(); } } class Student{ public Student(){ System.out.println("构造方法"); } //构造代码块 { System.out.println("构造代码块"); } static{ System.out.println("静态代码块"); } } /* 结果 10 静态代码块 构造代码块 构造方法 ------------ 构造代码块 构造方法 */
方法执行顺序:
1.静态代码块,随着类加载而加载,且只执行一次
2.构造代码块,每创建一个对象就会执行一次,优先于构造方法执行
3.构造方法,每创建一个对象就会执行一次
静态代码块>构造代码块>构造方法
练习:请问下面代码的运行结果是什么?
class Teacher { static { System.out.println("Teacher 静态代码块"); } { System.out.println("Teacher 构造代码块"); } public Teacher() { System.out.println("Teacher 构造方法"); } } public class TeacherTest { static { System.out.println("TeacherTest静态代码块"); } public static void main(String[] args) { System.out.println("main方法"); Teacher t1 = new Teacher(); Teacher t2 = new Teacher(); } } /* 答案 TeacherTest静态代码块 main方法 Teacher 静态代码块 Teacher 构造代码块 Teacher 构造方法 Teacher 构造代码块 Teacher 构造方法 */
8、继承
什么是继承?
继承是面向对象三大特征之一。java中的继承描述的是两个类之间的关系,被继承的类称为父类,继承的类称为子类,使用extends关键字来表示。在java语言里面只支持单继承,即一个类只能有一个父类,子类可以继承父类中的非private修饰的成员方法和成员变量,构造方法不能被继承,java里面的继承跟现实生活中的继承颇为相似,现实生活中一个儿子只能有一个父亲,儿子可以继承父亲的房子车子但是不能继承父亲大脑里面的思想和知识。如果一个类没有显示的继承其他类,那么这个类会默认继承Object类,Object是SUN公司提供的java中的根类。
继承的优点
- 提高了代码的复用性
- 提高了代码的维护性
- 让类与类之间产生了关系,是多态的前提
继承的缺点
增强了类之间的耦合。
软件开发的一个原则是高内聚,低耦合。
内聚是一个模块内各个元素彼此结合的紧密程度
耦合是一个软件里面不同模块之间相互连接的数量
class SuperClass extends SupersuperClass{ public void m1(){ System.out.println("SuperClass中的m1方法"); } private void m2(){ System.out.println("SuperClass中的m2方法"); } } class SubClass extends SuperClass{ public void m3(){ System.out.println("SubClass中的m3方法"); } } class SupersuperClass{ public void m4(){ System.out.println("SupersuperClass中的m4方法"); } } public class Test01{ public static void main(String[] args){ SubClass s = new SubClass(); s.m1(); //s.m2(); 子类不能访问父类中private修饰的方法 s.m3(); s.m4(); // 子类可以访问祖先类中的方法 } }
9、重写(override)
什么是重写?
重写,也叫做覆盖,当父类中的方法无法满足子类需求时,子类可以将父类的方法进行重写编写来满足需求。比如孩子继承了父亲的房子,可以将房子重新装修。
方法重写的条件:
- 两个类必须是继承关系
- 必须具有相同的方法名,相同的返回值类型,相同的参数列表.
- 重写的方法不能比被重写的方法拥有更低的访问权限。
- 重写的方法不能比被重写的方法抛出更宽泛的异常。(关于异常后面的章节再讲。)
- 私有的方法不能被重写。
- 构造方法无法被重写,因为构造方法无法被继承。
- 静态的方法不存在重写。
- 重写指的是成员方法,和成员变量无关。
class Animal{ public void eat(){ System.out.println("吃饭饭"); } public void sleep(){ System.out.println("睡觉觉"); } // 构造方法 public Animal(){ System.out.println("喝水水"); } // 构造方法 public static void m1(){ System.out.println("喝水水"); } } class Cat extends Animal{ public void eat(){ System.out.println("吃猫粮"); // 重写 } // 不能写构造方法 //public Animal(){ // System.out.println("小猫喝水水"); //} // 静态方法不存在重写 public static void m1(){ System.out.println("小猫喝水水"); } } class Dog extends Animal{ public void eat(){ System.out.println("吃狗粮"); // 重写 } } class Animal_food{ public static void main(String[] args){ Cat c = new Cat(); c.eat(); c.m1(); //c.drink(); Dog d = new Dog(); d.eat(); } }
10、super关键字
什么是super?
super代表的是当前子类对象中的父类型特征。
什么时候使用super?
- 子类和父类中都有某个数据,例如,子类和父类中都有name这个属性。如果要再子类中访问父类中的name属性,需要使用super。例1
- 子类重写了父类的某个方法(假设这个方法名叫m1),如果在子类中需要调用父类中的m1方法时,需要使用super。例1
- 子类调用父类中的构造方法时,需要使用super。
注意:super不能用在静态方法中。
class Animal{ public String name = "动物"; public void eat(){ System.out.println("吃饭"); } public void sleep(){ System.out.println("睡觉"); } } class Dog extends Animal{ String name = "旺财"; public void eat(){ System.out.println("吃狗粮"); } public void m1(){ System.out.println(super.name);//调用父类中的name System.out.println(this.name); super.eat(); this.eat(); } } public class AnimalTest01{ public static void main(String[] args){ Dog d = new Dog(); d.m1(); } }
class Animal { //颜色 String color; //品种 String category; public Animal(){ System.out.println("Animal中的构造方法"); } public Animal(String color,String category){ this.color = color; this.category = category; } } class Dog extends Animal { public Dog(){ super("土豪金","藏獒");//手动调用父类中的有参构造方法给成员变量进行赋值 System.out.println("Dog中的构造方法"); } } public class AnimalTest01 { public static void main(String[] args) { Dog d = new Dog(); System.out.println(d.color); System.out.println(d.category); } }
小练习
1、请问下面程序输出的结果是什么?
class Fu{ public int num = 125; public Fu(){ System.out.println("fu"); } } class Zi extends Fu{ public int num = 256; public Zi(){ System.out.println("zi"); } public void show(){ int num = 512; System.out.println(num); System.out.println(this.num); System.out.println(super.num); } } public class Test { public static void main(String[] args) { Zi z = new Zi(); z.show(); } }
答案:
fu
zi
512
256
125
分析:
创建Zi的对象之后系统会先调用Zi的父类Fu中的构造方法,所以先打印了fu
之后系统调用Zi中的构造方法,打印出zi
在show方法里面有个局部变量num=512,第一行打印num,调用的是局部变量,结果是512
通过this调用了成员变量num,打印出256
通过super调用父类中的num,打印出125
2、请问下面程序输出的结果是什么?
public class Test2_Extends { public static void main(String[] args) { Zi z = new Zi(); } } class Fu { static { System.out.println("静态代码块Fu"); } { System.out.println("构造代码块Fu"); } public Fu() { System.out.println("构造方法Fu"); } } class Zi extends Fu { static { System.out.println("静态代码块Zi"); } { System.out.println("构造代码块Zi"); } public Zi() { System.out.println("构造方法Zi"); } } 答案: 静态代码块Fu 静态代码块Zi 构造代码块Fu 构造方法Fu 构造代码块Zi 构造方法Zi 分析: 1.系统将Fu.class和Zi.class分别加载到方法区的内存里面,静态代码会随着.class文件一块加载到方法区里面,所以先打印出了静态代码块中的内容。 2.构造代码块优先于构造方法执行,父类初始化之前,所以打印出父类中的构造代码块和构造方法中的内容。