一,封装
1,概念
将数据与操作数据有机结合起来,影藏对象的属性和实现细节,仅对外公开实现接口和对象进行交互。
2,四种访问权限概述
public:不同包中的非子类
protected:可访问不同包中的子类
默认权限:包访问权限,可访问同一包中的不同子类
private:类访问权限
一般情况下,建议成员变量用provide修饰,而成员方法用public修饰(当然,这只是一个偷懒的方法,具体情况还要具体分析)
3,包
(1)导入包
一般使用import来导入java自带的包:
import static java.lang.Math.pow; import static java.lang.Math.sqrt; double result = sqrt(pow(3, 2) + pow(4, 2)); System.out.println(result);
在使用sqrt,pow时,需要导入包,两者都属于java.lang.Math这个包,sqrt,pow又分别属于Math包下的pow包和sqrt包。当然这里也可以直接写为以下这种形式,*则代表Math中的所有包,但也要注意这种写法,会出现两个不同包下有分别有一个名称相同的子包,这时编译器则不知道这个相同名称的子包是哪个包里面的。
import static java.lang.Math.*;
导入包时也可写为这种写法:int [] array=new int[]{1,2,3,4,5};
System.out.println(java.util.Arrays.toString( array));
但这种写法会显得代码冗杂,推荐第一种写法。
(2)自定义包
一般自定义包前有package,例如:
包名的写法:
1,包名以小写字母来书写
2,域名逆置写法,例如想要建立如下的包时,应写为com.baidu.www
4,static成员
1,如果一个对象用static修饰的话,那么这个成员变量就不属于这个对象,而在方法区里,生命周期伴随类的一生。
补充“方法区”:JVM里面包含了五大区,分别为:Java虚拟机,本地方法栈,堆(储存对象),方法区,程序计算器(储存下一条指令)
2,static修饰的变量为类变量,不属于某个对象,是所有对象共享的,且只有一份。
注:静态变量不依赖于对象。故静态方法中不需要对象,也不能在静态方法中调用对象,若真的需要再静态方法中调用对象,可以通过实例化一个新对象来实现。
3,访问静态成员变量时,可通过对象的引用访问,也可用过类名访问,推荐使用类名访问,因为静态成员变量是类类型变量。
4,静态方法:是类方法,类方法中不能访问非静态修饰的成员变量。
5,代码块
(1)普通代码块
普通代码块;定义在方法中的代码块,直接使用{}来定义。
public static void main2(String[] args) { { //定义在方法里面的,称为普通代码块 } }
(2)构造代码块
构造代码块:又称为实例代码块,非静态代码块,一般用于初始化成员变量
(3)静态代码块
静态代码块:用static修饰的代码块,只被执行一次。
例如: ..... public Student(String name, int age) { this.name = name; this.age = age; System.out.println("带参数的构造方法"); } { System.out.println("构造代码块"); this.age=15; } static{ System.out.println("静态代码块"); classroom="二年级三班"; }实例化两个对象: Student student1=new Student("lihua",12); Student student2=new Student("xiaofang",11); ...... 运行结果: 静态代码块 构造代码块 带参数的构造方法 构造代码块 带参数的构造方法
通过上述例子可得出:
1,当实例化有多个对象时,静态代码块只执行一次,有多个静态代码块时按顺序执行
2,构造代码块优先于构造方法(静态代码块>实例化代码块>构造方法)
6,内部类
可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类。内部类也是封装的一种体现。
定义在A 类中的B类,此时B类称之为内部类,A类称之为外部类。
(1)实例内部类
class OuterClass{ public int data1=1; public int data2=2; public int data3=3; class InnerClass{ public int data1=100; private int data5=5; public static int data6=6; public void test2(){ System.out.println("InnerClass"); System.out.println(this.data1); System.out.println(OuterClass.this.data1); System.out.println(data2); System.out.println(data3); System.out.println(data5); System.out.println(data6); } } public void test1(){ System.out.println("OuterClass"); } }
1,实例化内部类时,要先实例化外部类,再用外部类对象的引用去调用内部类
OuterClass out=new OuterClass(); OuterClass.InnerClass innerClass=out.new InnerClass(); 或者写为: OuterClass.InnerClass innerClass1=new OuterClass().new InnerClass();
2,当内部类与外部类的某一个成员变量相同时,内部类会优先调用内部类的成员变量,所以该语句System.out.println(this.data1);的运行结果为100。如果此时想要访问外部类相同名称的成员变量时,应写为<外部类类名.this.成员变量>;例如: System.out.println(OuterClass.this.data1);
(2)静态内部类
class OuterClass1{ public int data1=1; public int data2=2; public static int data3=3; static class InnerClass{ public int data1=100; private int data5=5; public static int data6=6; public void test1(){ System.out.println("InnerClass"); System.out.println(new OuterClass().data2); System.out.println(data3); System.out.println(data5); System.out.println(data6); //静态的不能访问外部类的成员变量 } } public void test1(){ System.out.println("OuterClass"); } }
1,静态内部类可访问外部静态成员变量
2,静态内部类的实例化的写法为(相当于静态内部类作为一个外部类的静态成员方法,所以用<外部类名称.静态类名称>):
OuterClass1.InnerClass innerClass=new OuterClass1.InnerClass();
3,静态内部类不能直接访问外部类的成员变量,如果要访问可在内部类内部实例化一个外部类的对象 ,例如:System.out.println(new OuterClass().data2);
(3)局部内部类
class OuterClass2 { public int data1 = 1; public int data2 = 2; public static int data3 = 3; public void test(){ class InnerClass{ public int data1=100; private int data5=5; public static int data6=6; public void test(){ System.out.println(data1); } } InnerClass innerClass=new InnerClass(); innerClass.test(); //局部内部类只能在当前方法中使用 } }
局部内部类为,在外部类的成员方法中定义一个类,因此局部内部来只能在成员方法中使用。实例化也需要在该成员方法中进行。
(4)匿名内部类
interface ITestable{ void test(); } public class Text5 { public static void main(String[] args) { new ITestable(){ @Override public void test() { System.out.println("test()"); } }.test(); } public static void main1(String[] args) { ITestable it=new ITestable(){ @Override public void test() { System.out.println("test()"); }//并没有实例化接口//相当于一个类实现了这个接口同时重写了test这个方法 }; it.test(); } }
匿名内部类多用于接口
new ITestable(){ @Override public void test() { System.out.println("test()"); }此处并没有实例化接口,相当于有一个类实现了这个接口同时重写了test()这个方法,又因为这个类没有具体的名称,所以是一个匿名类。
此时想要访问成员变量有两种方法:第一种,在实例化的对象后面直接< .访问的对象>,第二种,写出实例化的引用,<引用 . 访问对象>
二,继承
1,基本概念
它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承主要解决的问题是:共性的抽取,实现代码复用。
2,基本语法
以猫和狗举例,猫和狗有共性部分,例如都具有名字,年龄等。因此可以建一个类,这个类里面包含了猫狗类中共性的部分,我们将其称为父类,而猫狗我们称之为子类,我们让子类继承于父类,可有效地减少代码量。
1,子类继承于父类,我们用关键字extends。写法为<子类extends父类>,一子类可继承多个父类,可写为<子类extends父类1,父类2...>(补充:子类又可称之为派生类,父类又可称之为基类,超类)
2,访问成员变量时,会优先访问子类,若子类没有则会访问父类,若父类也没有,则会报错。
若子类与父类的成员变量名称相同时,会遵循上述原则,先访问子类,但若此时想访问父类,则需要使用关键字super。
class Base{ public int a=1; public int b=2; } class Derived extends Base{ public int a=100; public int c=3; public void test(){ System.out.println(super.a); System.out.println(this.a); System.out.println(this.b); System.out.println(this.c); } } 遵循优先访问子类,在访问父类的原则,第一个:运用了super访问了父类的a,为1,第二个,访问子类,为100,第三个先访问子类,子类没有,在访问父类,为2,第四个,访问子类,为3.
4,this与super
不同点:
(1)this访问成员变量,super访问父类成员
(2)this调用方法,super调用父类方法
class Base{ public void test1(){ System.out.println("父类test1"); } } class Derived extends Base{ public void test1(){ System.out.println("子类test1"); } public void test2(){ this.test1(); super.test1(); } }
(3)调用当前类中的其他构造方法,super调用父类的构造方法
当子类继承父类时,在实例化子类对象是,要先帮助父类进行构造(初始化父类成员),初始化父类成员,只有一种方法调用构造方法。
当给父类写构造方方法时,编译器会默认存在一个没有参数的构造方法,与此同时,子类里也会有一个没有参数的构造方法,且该构造方法中存在一个<super();>,为对父类构造方法的调用。
子类调用父类的构造方法时,用super(成员变量1,成员变量2...),该语句要写在子类构造方法的第一行,与this调用有参数的构造方法方法相似。
相同点:
1,均不能在静态方法中访问,因为this,和super都是指向对象的,而静态方法与对象无关
2,this(),和super()都要放到首行,故两者不能同时存在
3,两者都是关键字
总结:代码执行时,父类和子类的静态方法先执行>父类实例代码块与构造方法>子类实例代码块与构造方法
class Animal{ public String name; public int age; public String color; public Animal(String name, int age, String color) { this.name = name; this.age = age; this.color = color; }//先执行父类的构造方法在执行子类的 static { System.out.println("Animal::static"); } { System.out.println("Animal::{}"); } public void eat(){ System.out.println(this.name+" is eating"); } } class Dod extends Animal{ public Dod (){ super("dahuang",10,"黄色"); //必须在第一行 } static { System.out.println("Dod::static"); } { System.out.println("Dod::{}"); } public void bark(){ System.out.println(this.name+"汪汪汪"); } } public class Text { public static void main(String[] args) { Dod dog=new Dod(); dog.eat(); dog.bark(); } }
3,final关键字
1,可以用来修饰变量,成员方法以及类
2,修饰的变量或字段,表示为常量,不能被修改
3,修饰类,表示此类不能被继承,被称之为密封类
4,修饰方法,这种方法不能被重写
4,继承和组合
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
class Tire{ } class Engine{ } class VehicleSystem{ } class Car{ private Tire tire; private Engine engine; private VehicleSystem vs; // ... } class Benz extends Car{ // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来 }
三,多态
1,基本概念
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状 态。
2,实现条件
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
重写又被称为覆盖,复写:
1,方法名相同 2,方法的参数列表相同 3,方法的返回值相同 必须发生向上转型,且通过父类引用调用了父类与子类同名的方法重写
例如:
class Animal{
public String name;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(this.name +" is eating");
}
}
class Dog extends Animal{
public Dog(String name, int age) {
super(name, age);
}
public void bark(){
System.out.println(this.name+" 汪汪叫");
}
@Override
public void eat(){
System.out.println(this.name+"正在吃狗粮");
}
}
class Bird extends Animal{
public Bird(String name, int age) {
super(name, age);
}
public void qiqi(){
System.out.println(this.name+" 吱吱叫");
}
@Override
public void eat() {
System.out.println(this.name+"正在吃鸟粮");
}
public void fly(){
System.out.println(this.name+"正在飞");
}
}
@Override为注解,修饰该方法,该方法是被重写的。作用:帮你对重写进行检查
注意:
1,静态方法不能被重写
2,被private修饰的不能被重写
3,被final修饰的不能被重写
4,重写时,子类的权限要大于父类的权限
5,重写的返回值可以是父子关系
public Animal eat(){
System.out.println(this.name +" is eating");
return null;
}
public Dog eat(){
System.out.println(this.name+"正在吃狗粮");
return null;
}
3. 通过父类的引用调用重写的方法
public static void main1(String[] args) { Animal animal1 = new Dog("小黄",12); animal1.eat();
这里发生了向上转型,向上转型分为三个时机;
1,直接赋值型:
Animal animal1 = new Dog("小黄",12);
2, 方法传参型:
public static void func1(Animal animal1){ } public static void main2(String[] args) {//发生向上转型的时机 func1(new Dog("小黄",12)); }
3,返回值型:
public static Animal func2(){ return new Dog("小黄",12); }
综上所述:
public static void func(Animal animal1){ animal1.eat(); } public static void main3(String[] args) { func(new Dog("小黄",12)); func(new Bird("小小鸟",10)); } 当animal引用的对象不同,调用eat的表现行为不一样,这就称为多态
向上转型不能调用到子类特有的方法。
向下转型:将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的 方法,此时:将父类引用再还原为子类对象即可,即向下转换。
Animal animal1 = new Dog("小黄",12); 不能写成Dog dog=animal1(把大范围给到了一个小范围) Dog dog=(Dog)animal1;所以要强制类型转换 dog.bark();
我们通常书写为:
if (animal1 instanceof Bird){ Bird bird=(Bird) animal1; bird.fly(); } else { System.out.println("animal1 instanceof Bird not"); }
animal1 instanceof Bird这一步是为了提高向下转型的安全性。如果表达式为真,则可以进行转换
3,多态的优缺点
优点:
1,降低代码的复杂度,避免大量使用if-else
2,可扩展能量强
例如:
class Shape{ public void draw(){ System.out.println("画一个图形"); } } class Rect extends Shape{ public void draw(){ System.out.println("画一个矩形"); } } class Cycle extends Shape{ public void draw(){ System.out.println("画一个圆圈"); } } class Tri extends Shape{ public void draw(){ System.out.println("画一个三角形"); } }
public static void drawMaps(){ Rect rect=new Rect(); Cycle cycle=new Cycle(); Tri tri=new Tri(); Shape [] shapes={rect,rect,cycle,tri}; for (Shape shape:shapes){ shape.draw(); } } public static void main(String[] args) { drawMaps(); }
缺点:代码运行效率低