Java 三大特性,算是Java独特的表现,提到Java 的三大特性, 我们都会想到封装, 继承和多态 这是我们Java 最重要的特性。
封装(Encapsulation) :
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
• 将变化隔离。
• 便于使用。
• 提高重用性。
• 提高安全性。
封装原则:
• 将不需要对外提供的内容都隐藏起来。
• 把属性都隐藏,提供公共方法对其访问 。
private关键字:
• 是一个权限修饰符。
• 用于修饰成员(成员变量和成员函数)
• 被私有化的成员只在本类中有效。
常用之一:
将成员变量私有化,对外提供对应的set , get方法对其进行访问。提高对数据访问的安全性。
举个栗子:
我们常说的失血模型
public class Demo { private String name;
private String sex ;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }
构造代码块和构造方法(Construct):
构造方法:
用于给对象进行初始化,是给与之对应的对象进行初始化,它具有针对性,函数中的一种。
特点:
- 该函数的名称和所在类的名称相同。
- 不需要定义返回值类型。
- 该函数没有具体的返回值。
- 构造函数并不是由我们手动调用的(手动调用指的是如b1.baby();),而是在创建对应的对象时,JVM就会主动调用到对应的构造函数。
- 如果一个类没有显式的写上一个构造方法时,那么Java编译器会为该类添加一个无参的构造函数的。
- 如果一个类已经显式的写上一个构造方法时,那么Java编译器则不会再为该类添加一个无参的构造方法。
- 构造函数是可以在一个类中以函数重载的形式存在多个的。
- 构造方法有无参构造方法和有参构造方法。无参构造方法JVM 默认创建一个,如果手动创建了有参构造方法,那么系统会默认识别有参构造方法。
构造函数的定义格式:
修饰符 函数名(形式参数){
函数体;
}
修饰符 函数名(){
函数体;
}
构造方法与普通方法的区别:
(1)返回值类型的区别:
①构造函数是没有返回值类型的,如果加上void或者别的返回值类型,就变成一个普通的函数,就需要我们手动去调用。
②普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。
(2)函数名的区别:
①构造函数的函数名必须要与类名一致。
②普通函数的函数名只要符合标识符的命名规则即可。
(3)调用方式的区别:
①构造函数是在创建对象的时候由JVM调用的。
②普通函数是由我们使用对象调用的,一个对象可以调用多次普通的函数。
(4)作用上的区别:
①构造函数的作用用于初始化一个对象。每创建一个对象就会有一个初始值。
②普通函数是用于描述一类事物的公共行为的。
注意事项:
(1)Java编译器添加的无参的构造方法的权限修饰符与类的权限修饰符是一致的。
(2)构造函数是创建对象的时候调用的,所以就可以把属性值传到构造函数中,在构造函数中设置参数接收属性值。
(3)JVM和Java编译器是不同的,Java编译器编译生成的.class文件是给JVM看的,所以经过编译后的class类打开后会是乱码,我们可以通过反编译来查看。
构造代码块:
1.构造代码块的作用与构造函数的作用的对比:
(1)构造代码块的作用:给所有对象进行统一的初始化,对象一建立就运行并且优先于构造函数,比如所有的婴儿出生都会哭。
(2)构造函数的作用:给对应的对象(new )进行初始化。
构造代码块的格式: { 构造代码块; }
注意:构造代码块的大括号必须位于成员的位置上。
代码块的类别:
(1)构造代码块:在工作中经常会用到。
(2)局部代码块:大括号位于方法之内,基本上写不写没什么区别,现实开发中也很少会用到。它的作用是缩短局部变量的生命周期,节省一点点内存。
(3)静态代码块:使用static修饰的代码块。
注意的事项:
(1)Java编译器在编译一个Java源文件的时候,会把成员变量的声明语句提前至一个类的最前端。
(2)成员变量的初始化工作其实都是在构造函数中执行的。
(3)一旦经过Java编译器编译后,那么构造代码块的代码就会被移动到构造函数中执行,构造代码块的代码是在构造函数之前执行的,构造函数中的代码是最后执行的。
(4)成员变量的显示初始化与构造代码块的代码是按照当前代码的顺序执行的。
继承(inheritance):
继承是面向对象最显著的一个特性。 继承是从已有的类中派生出新的类, 新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
在JAVA中, 被继承的类叫父类(parent class)或超类(superclass), 继承父类的类叫子类(subclass)或派生类(derivedclass)。 因此, 子类是父类的一个专门用途的版本, 它继承了父类中定义的所有实例变量和方法, 并且增加了独特的元素 。
继承的结构:
继承的使用 :
关键字:extends。
使用继承
– 编写父类
– 编写子类, 继承父类
class Animal {
//公共的属性和方法
}
class Chicken extends Animal{
//子类特有的属性和方法
}
class Duck extends Animal {
}
基本语法:
基本语法 class Chicken extends Animal{ } 上述代码表示Chicken类继承Animal类,使用extends关键词将Animal类(父类/超类)和Chicken类(子类)连接接起来;
在继承关系下,Chicken类将拥有Animal类所有的非私有的方法和属性,Chicken类还可以拥有自己独有的方法和属性;
声明Animal类,实例化Chicken类时, Chicken类会自动向上转型为Animal类;
举个栗子:
//创建动物类 public class Animal {
private String type;
private String skin;
private int legCount;
public void eat(){
System.out.println("动物正在吃东西");
}
public void breath(){
System.out.println("动物正在呼吸");
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSkin() {
return skin;
}
public void setSkin(String skin) {
this.skin = skin;
}
public int getLegCount() {
return legCount;
}
public void setLegCount(int legCount) {
this.legCount = legCount;
}
} //鸡类
public class Chicken extends Animal {
public void eat(){
System.out.println(“鸡正在吃东西”);
}
public void run(){
System.out.println(“鸡在跑");
}
} //鸭类
public class Duck extends Animal {
public void eat(){
System.out.println(“鸭正在吃东西”);
}
public void run(){
System.out.println(“鸭在跑");
}
} //测试类 public class Test {
public static void main(String[] args){
Chicken chicken=new Chicken ();
chicken.eat();
chicken.setType(“鸡”);
chicken.setSkin(“金色");
chicken.setLegCount(2);
System.out.println("动物品种是: "+chicken.getType()+", 肤色是: "+chicken.getSkin()+", 腿数"+t.getLegCount());
chicken.run();
Duck duck =new Duck ();
duck.eat();
duck.fight();
}
}
继承执行的顺序:
java中, new一个类的对象, 类里面的静态代码块、 非静态代码块、无参构造方法、 有参构造方法、 类的一般方法等部分, 它们的执行顺序相对比较简单, 例如:
public class FatherTest{
private String name;
public FatherTest(){
System.out.println(“--父类的无参数构造方法--”);
}
public FatherTest(String name){
System.out.println(“--父类的有参数构造方法--”+this.name);
}
static{
System.out.println(“--父类的静态代码块--”);
}
{
System.out.println(“--父类的非静态代码块--”);
}
public void speak(){
System.out.println(“--父类的方法--”);
}
} public static void main(String[] args){
System.out.println(“--父类主程序--”);
FatherTest father = new FatherTest(“父亲的名字”);
father.speak();
} 执行结果为:
--父类的静态代码块--
--父类主程序--
--父类的非静态代码块--
--父类的有参构造函数--父亲的名字
--父类的方法-- 执行顺序总结:
静态代码块—>主程序—>非静态代码块—>构造函数—>一般方法
加入子类继承后的执行顺序, 例如 :
public class SonTest extends FatherTest{
private String name;
static{
System.out.println("--子类的静态代码块--"); }
{
System.out.println("--子类的非静态代码块--");
}
public SonTest(){
System.out.println("--子类的无参构造方法--");
}
public SonTest(String name){
System.out.println("--子类的有参构造方法--"+name);
}
@Override
public void speak() { System.out.println("--子类重写了父类的方法--"); }
} public static void main(String[] args) {
System.out.println("--子类主程序--");
FatherTest father=new FatherTest("父亲的名字");
father.speak();
SonTest son=new SonTest("儿子的名字");
son.speak();
} 执行结果为:
--父类的静态代码块--
--子类的静态代码块--
--子类主程序--
--父类的非静态代码块--
--父类的有参构造函数--父亲的名字
--父类的方法--
--父类的非静态代码块--
--父类的无参构造函数--
--子类的非静态代码块--
--子类的有参构造方法--儿子的名字
--子类重写了父类的方法--
方法的重写:
方法重写是在继承关系下, 子类拥有与父类方法名、 参数(个数、顺序、 类型)、 返回值类型完全相同, 访问修饰符只能扩大或相等, 不可缩小, 但实现过程与父类不同的方法。 方法重写也是多态的一种变现形式。
重写必须满足以下几个条件:
在子类中可以根据需要对从基类中继承来的方法进行重写;
重写的方法和被重写的方法必须具有相同方法名称、 参数列表和返回类型;
重写方法不能使用比被重写的方法更严格的访问权限 ;
举个栗子:
//鸡类
class Chicken extends Animal {
public void eat(){
System.out.println(“鸡正在吃东西”);//对父类Animal中的eat方法进
行重写
}
public void run(){
System.out.println(“鸡在跑");//可以添加新的方法
}
}
Super关键字:
super关键字是一个特殊的变量, 它提供了对父类的方法。 可以用super主动调用父类的构造方法、 访问父类中的成员。
super调用父类的构造方法:
public class Duck extends Animal {
public Duck(String name){
super(name);//主动调用父类的构造方法
}
}
super访问父类的成员:
在子类方法中使用super访问父类中隐藏的成员, 访问形式是:
super.变量;
super.方法名(参数);
public class Duck extends Animal {
@Override
public void eat() {
System.out.println();
}
public void quack(){
System.out.println(super.name);//使用super调用父类的属性
eat();
super.eat();//使用super调用父类的eat()方法
}
public static void main(String[] args) {
new Duck().quack();//创建Duck类对象并调用quack方法
}
}
final 关键字:
“final”关键字用来修饰类、 方法和变量, 其含义“不可改变的、 最终的”。
修饰类 声明为final的类不能派生子类,即此类不能被继承;
public final class Person{ }
修饰变量 表示它为一个常量,变量一旦初始化,将不能改变;
final int COUNT = 5;
修饰方法 表示向编译器表明子类不能重写此方法;
public final void eat(){ }
多态(polymorphism)
在面向对象语言中, 多态性是指一个方法可以有多种实现版本,即“一种定义, 多种实现”。 利用多态可以设计和实现可扩展的系统, 只要新类也在继承层次中。 新的类对程序的通用部分只需进行很少的修改, 或不做修改。 类的多态性表现为方法的多态性,方法的多态性主要有方法的重载和方法的覆盖。
重载:
方法重载(overload)是指在同一个类中的多个方法可以同名但参数列表必须不同。 重载表现为同一个类中方法的多态性。
class Chicken {
public void eat(){
System.out.println(“鸡正在吃东西”);
}
public void eat(String food){
System.out.println(“鸡在吃"+food);//重载eat方法
}
}
方法重写(override)是指子类冲定义了父类中同名的方法。 重写表现为父子与子类之间方法的多态性。
//鸡类
class Chicken extends Animal {
public void eat(){
System.out.println(“鸡正在吃东西”);//对父类Animal中的eat方法进
行重写
}
}
对象类型转换:
基本类型的数据可以转换类型, 当转换类型较高时可以自动转换, 当转换类型较低时需要强制转换。 对象类型也允许转换, 这个转换只限于java类层次结构图上的一根枝干上, 即父类和子类之间。 枝干上离Object较近的为上, 相反较远的为下, 由此对象的类型转换分为“向上转型”和“向下转型”两种。
public class Duck extends Animal {
@Override
public void eat() {
System.out.println();
}
public void quack(){
System.out.println("嘎嘎嘎");
}
public static void main(String[] args) {
Animal a1 = new Animal();//实例化过程
Animal a2 = new Duck();//向上转型
a1.eat();
a2.eat();
//a2.quack();//去除注释会怎样?
}
}
向下转型:只能针对指向子类对象的基类对象引用进行 。
public class Duck extends Animal {
@Override
public void eat() {
System.out.println();
}
public void quack(){
System.out.println("嘎嘎嘎");
}
public static void main(String[] args) {
Animal a = new Duck();//向上转型
Duck b = (Duck) a;//向下转型
a.eat();
b.eat();
b.quack();
}
}
Instanceof 关键字 :
instanceof关键字是用来判断其左边对象是否为其右边的实例, 返回boolean类型的数据 .
boolean result = Object instanceof class
同时, 也可以用来判断继承中的子类的实例是否为父类的实现。
.....
public static void main(String[] args) {
Animal a = new Duck();//向上转型
if(a instanceof Duck){
((Duck) a).quack();
}
}
.....