面向对象
面向对象的本质就是:以类的方式组织代码,以 对象的方式封装数据
new关键字
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化以及调用构造方法
构造方法
创建对象的时候自动调用(无参构造),主要作用是为了初始化
- 必须和类的名字相同
- 必须没有返回值,不能写void
构造方法的重载
- 构造方法重载成有参构造之后,如果还需要使用无参构造,必须重新建里无参构造
在IDEA中使用快捷键alt+insert可以快速添加重载构造方法以及get,set方法
静态方法和非静态方法
- 静态方法:用static修饰,可以直接类名.方法进行调用,从属于类,和类一起加载。
- 非静态方法:没有用static进行修饰,调用时必须创建对象,从属于对象,和对象一起加载。
- 静态方法在访问本类的成员时只允许方法问静态成员(静态方法和静态成员变量),不能访问对非静态成员变量和非静态成员方法。而非静态方法可以访问本类的静态成员。主要原因是因为,静态方法是和类一起加载的,非静态方法是和对象一起加载的,对象就是类的实例化,因此在没有实例化之前是无法调用非静态成员的,所以静态方法无法调用非静态成员,同时非静态方法又称为实例方法。
封装
-
高内聚,低耦合——程序设计需要追求高内聚,低耦合。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉。低耦合仅暴露少量的方法给外部使用。
-
封装:应禁止直接访问一个对象中数据的实际表现,而应通过操作接口来访问,这成为信息隐藏。
-
属性私有,get/set方法
封装的作用
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护增加了
继承
- Java中只有单继承是没有多继承的
- 继承是类和类之间的一种关系。除了继承之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends表示
- 子类和父类的关系,Student继承自Person,Student是Person的子类
- final修饰的类无法继承
public class Person{
}
//Student继承父类Person
public class Student extends Person{
}
- 子类会拥有父类的方法和属性,但是不会继承私有方法和属性
- 在Java中所有的类都直接或者间接继承Object
父类和子类的构造方法以及super详解
//调用父类的构造方法必须卸载子类的构造方法的第一行
public class Person{
public Person() {
System.out.println("Person");
}
}
//Student继承父类Person
public class Student extends Person{
public Student(){
//这里隐藏了super(),会调用父类的无参构造
//super();
System.out.println("Student");
//不能在这里调用父类构造方法super()
}
}
//若父类只有有参构造,那么子类就无法使用父类无参构造,必须在子类的无参构造中写出父类有参构造
public class Person{
//这里写了有参构造,无参构造就没了,需要重新写无参构造
public Person(String name) {
System.out.println("Person");
}
}
//Student继承父类Person
public class Student extends Person{
//这里会报错,因为父类是有参构造,子类是无参构造,必须要写出父类的有参构造
public Student(){
//这里隐藏了super(),会调用父类的无参构造
//super();
super("name");
System.out.println("Student");
//不能在这里调用父类构造方法super()
}
}
super必须只能出现在子类的方法或者构造方法中。
super和this不能同时调用构造方法。
- this:本身调用者这个对象。super:代表父类对象的引用
- this:没有继承也可以使用。super:只能在继承条件才可以使用
- this();本类的构造。super();父类的构造
方法的重写
方法重写和重载完全不同,重载是同类中,方法名相同,参数类型不同,而重写是子类重写父类方法,方法名相同,参数类型相同,修饰符范围可以扩大但是不能缩小,抛出的异常范围可以缩小但是不能扩大。
//静态成员可以被继承,但是不会被重写,如果子类有同名的静态成员,那么父类的静态成员会被隐藏
public class A {
static String str = "A静态成员";
String str1 = "A非静态成员";
static public void staticPrint() {
System.out.println("A静态方法");
}
public void print() {
System.out.println("A非静态方法");
}
}
public class B extends A{
static String str = "B静态成员";
String str1 = "B非静态成员";
static public void staticPrint() {
System.out.println("B静态方法");
}
public void print() {
System.out.println("B非静态方法");
}
}
public class C extends A{
}
//主函数
public static void main(String[] args) {
C c = new C();
System.out.println(c.str);
System.out.println(c.str1);
c.staticPrint();
c.print();
//A静态成员
//A非静态成员
//A静态方法
//A非静态方法
A b = new B();
System.out.println(b.str);
System.out.println(b.str1);
b.staticPrint();
b.print();
//B静态成员
//B非静态成员
//B静态方法
//B非静态方法
}
为什么需要重写
- 父类的功能,子类不一定需要或者不一定满足!
多态
一种方法可以根据发送对象的不同而采用多种不同的行为方式。
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
- 多态是方法的多态,属性没有多态
//People类
public class People {
public void eat() {
System.out.println("吃饭");
}
}
//学生类
public class Student extends People{
@Override
public void eat() {
System.out.println("吃水煮肉片");
}
public void study() {
System.out.println("学习");
}
}
//测试
public class Demo05 {
public static void main(String[] args) {
People p = new Student();
p.eat();//输出水煮肉片
((Student) p).study();//要调用study()方法需要转换p的类型
}
}
instanceof关键字
用来判断对象和类之间的继承关系
//Person->Student
//Person->Teacher
Object object = new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof People );//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
People people = new Student();
System.out.println(people instanceof Student);//true
System.out.println(people instanceof People );//true
System.out.println(people instanceof Object);//true
System.out.println(people instanceof Teacher);//false
//System.out.println(people instanceof String);//编译报错,因为People和String都是继承Student的
子类和父类的转换
- 父类引用指向子类的对象
- 把子类转换为父类,向上转型
- 把父类转换成子类,向下转型,强制转换
- 方便方法的调用,减少重复的代码!
//子类转换成父类,可能会丢失自己本来的一些方法
//父 子
Person student = new Student();
//强制转化,将父类转成子类
Student s = (Student) student;
抽象类
- abstract修饰的类称为抽象类
- abstract修饰的方法称为抽象方法
- 子类继承抽象类,必须重写抽象类中的所有抽象方法
- 不能new这个抽象类,只能靠子类去实现它。但是抽象类有构造器,只是不能直接创建抽象类的实例对象。在继承抽象类的子类中通过super()可以调用抽象类中的构造方法
- 抽象类中可以写普通方法
- 抽象方法必须在抽象类中
- 抽象类存在的意义:更利于代码的维护和重用
接口(真的很关键)
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有!
- 接口:只有规范!自己无法写方法专业的约束约束和实现分离,面向接口编程
- 接口中的所有定义方法都是抽象的public abstract,所有的变量都定义成常量public static final
- 接口可以用implements继承多个接口
内部类
内部类就是在一个类的内部定义了一个类。比如,A类中定义了一个B类,那么B类相对于A类来说称为内部类,而A类相对于B类来说就是外部类了
- 成员内部类
public class Outer {
private int id = 10;
public void out() {
System.out.println("这是外部类方法");
}
//成员内部类用static修饰之后无法调用外部类的非静态成员
class Inner{
public void in(){
System.out.println("这是内部类方法");
}
public void getID(){
System.out.println(id);
}
}
}
public class Demo10 {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类来实例化内部类~
Outer.Inner inner = outer.new Inner();
inner.getID();
}
}
一个Java文件中可以有多个class类,但是只能有一个public类
- 局部内部类
public class LocalInnerClass {
public static void main(String[] args) {
//定义局部内部类
class InnerBase{
int a;
}
class InnerSub extends InnerBase {
int b;
}
InnerSub is = new InnerSub();
is.a = 1;
is.b = 2;
System.out.println("a = "+ is.a + " b = "+ is.b);
}
}
- 匿名内部类
public class Demo10 {
public static void main(String[] args) {
//匿名对象
new Apple().eat();
//匿名内部类
//一般格式, new 类名/抽象类/接口(){};
new UserService(){
//重写接口中的方法
}
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
异常
- 异常处理五个关键词try,catch,finally,throw,throws
public class Demo01 {
public static void main(String[] args) {
int a = 1;
int b = 0;
//多个catch捕获异常必须要从小到大
try {
System.out.println(a/b);
}catch(ArithmeticException e) {//想要捕获的异常类型,最高级别异常类型Throwable
System.out.println("被除数不能为0");
e.printStackTrace();//打印当前异常错误信息
}finally{
System.out.println("finally");//无论是否捕获到异常都会运行
//finally可以不需要
}
}
}
- 抛出异常
//方法体内抛出异常throw
public class Demo02 {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
if (b == 0) {
throw new ArithmeticException();//主动抛出异常
}
System.out.println(a/b);
}finally{
System.out.println("finally");
}
}
}
//方法抛出异常
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
new Demo02().test(1,0);
}catch(ArithmeticException e) {
System.out.println("被除数不能为0");
}finally{
System.out.println("finally");
}
}
public void test(int a, int b) throws ArithmeticException{
System.out.println(a/b);
}
自定义异常
public class MyException extends Exception{
//传递数字>10
private int detail;
public MyException(int a) {
this.detail = a;
}
//toString:异常的打印信息
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
public class Test {
//可能会存在异常的方法
static void test(int a) throws MyException{
System.out.println("传递的参数为"+a);
if(a>10) {
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println("MyException=>"+e);
}
}
}
实际应用中的经验总结
- 在多重catch后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以假设try-catch,处理潜在的异常
- 尽量去处理异常,切记简单地调用printStackTrace()打印输出
- 尽量添加finally语句块去释放占用资源