面向对象
两种编程思想
面向过程POP: 所有步骤按顺序执行,注重执行的细节。
面向对象OOP: 创建解决问题的对象,让各个对象调用各自的方法而配合完成。
在面向对象编程OOP中,给类中定义的方法,具体实现过程,其实也是面向过程的
类与对象
对象object
某个类的具体实例
类Class
是拥有相同属性和行为的对象的集合,是对象的模板。
定义类
修饰符 class 类名{
//属性:定义变量
//行为:定义方法
}
类中的属性
成员变量、局部变量、静态常量
成员变量:定义在类中的变量,即类的属性,它有默认值,通过对象访问。
局部变量:定义在方法中的变量,它没有默认是,只能在方法中赋值后才能使用。
静态常量:特殊的成员变量,使用final static修饰。它有默认值。通过类名访问。
类中的方法
构造方法
成员方法
静态方法
class Person{
//成员变量
String name;
//静态常量
final static String contry="中国";
//构造方法
public Person(String name){
this.name=name;
}
//成员方法
void info(){
//局部变量
String address="悉尼";
System.out.println("我叫"+name+",来自于"+contry+",现居住:"+address);
}
//静态方法
public static void fun(){
}
}
创建对象
类名 变量名=new 构造方法([参数]]);
构造方法
是一种特殊的方法。方法名和类名一致,没有返回值部分。
访问修饰符 类名(数据类型 参数名称){
}
没有返回值部分,方法名必须和类名一致。
在使用new关键字创建对象时,调用对应的构造方法。
每个类在定义后,都隐藏有一个无参的构造方法。
如果自定义了有参数的构造方法,无参数的构造方法就会失效。如果想要使用无参构造方法,就要再写出来。
构造方法通常用于限制创建对象时携带的参数,初始化成员变量。
构造方法之间都是重载关系,构造方法不能重写。
构造方法执行时,不一定会创建对象。如抽象类中有构造方法,但无法创建抽象类对象,只能创建抽象类的子类对象时,自动调用抽象类的构造方法。
面向对象的三大特性
封装
将类中的属性使用private修饰,这样就能防止非当前类对其访问,隐藏类中的关键属性。
通常会对private修饰的属性提供公开的get 和set方法用于获取和赋值。
继承
类A extend 类B
接口A extend 接口B
前者就是后者的子类,前者的对象能直接访问后者中非私有成员。
重写Override
子类继承父类后,对父类中的非私有方法进行重写,达到扩展或重做的目的
必须满足方法名、返回值、参数列表都相同
访问权限不能比父类中的方法更严格
不能抛出比父类中的方法更大的异常
重载Overload
在一个类中,某个方法在不同的参属下,表现不同的行为。同名不同惨
方法名必须相同
参数列表必须不同(类型和数量)
无返回值
this和super
都可以当作对象后构造方法使用。
当作对象:this表示当前类的对象,super表示当前类的父类对象。
this 或super当作对象使用时,只能在非静态方法中。
当作构造方法
this()表示当前类中无参构造方法,如果带参数就表示对应参数的构造方法。
super()表示当前类的父类的无参构造方法,如果带参就表示对应参数的构造方法
this()或super()只能用在另一个构造方法的首行。
在继承后,如果子类和父类都没有写出任何构造方法,子类中有一个隐藏的无参构造方法,会调用父类的无参构造方法
public class Father{
}
public class Son extends Father{
/*
会有这段代码
*/
public son(){
super();
}
}
如果在父类中定义了有参数的构造方法,无参构造方法就会失效,子类必须调用父类中的有参构造方法
public class Father{
String name;
public Father(String name){
this.name=name;
}
}
public class Son extends Father{
/*
必须调用super(String name)
*/
public Son(String name){
super(name);
}
}
Object类
是所有类的父类。任何类都简洁的继承了Object类。所以所有类都能访问Object类中的方法,都可以进行重写
toString()是Object类中的一个方法,在输出对象时自动调用。
默认输出“类名限定名@十六进制哈希码”。通常在自定义的实体类中,重写toString(),输出当前类的属性。
equals()是Object类中的方法,用于比较两个对象是否相同
默认使用== 比较内存定制。通常在自定义的实体类中,重写equals(),自定义比较规则
hashCode()是Object类中的一个方法,用于获取对象的哈希码。
默认使用全部参数生成哈希码。可以重写自定义生成的哈希码的参数。
对象转型
类似于原始类型间的数据类型转换
向上转型:父类变量=子类对象;
类似于自动类型的转换
Person p=new Person()
object obj=p;
向下转型:子类变量=(子类)父类对象
类似于强制类型转换
Animal anl=new Animal();
Cat c=(Cat)anl;
多态
在继承关系中,子类的对象可以保存到父类的变量中。(向上转型)
多态常用于定义方法中,形参为一个父类或接口类型变量,实参为子类对象。
无法通过父类变量调用子类中独有的方法。如果重写了父类中的方法时,执行重写后的内容。
父类/父接口 对象 = new 子类();
public class Father{
public void somking(){
System.out.println("Father is smoking");
}
}
public class Son extends Father{
public void play(){
System.out.println("Son is playing");
}
public void smoking(){
System.out.println("Son is smoking");
}
}
public class Test{
public static void main(String[] args){
Father father=new Son();
//可以访问
father.smoking();//输出Son is smoking
//无法访问
//father.play();
}
}
修饰符
访问修饰符
当前类 | 当前包 | 不同包中的子类 | 不同包中的非子类(同一个项目) | |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
不写 | √ | √ | × | × |
private | √ | × | × | × |
final
修饰属性
变量为常量,定义时就要赋值,程序运行过程中不能改变其值。
修饰方法
方法成为最终方法,不能重写。
修饰类
类为最终类,不能被继承。
abstract
修饰方法
方法为抽象方法,没有方法体,同时所在的类也需要使用abstract定义为抽象类
修饰类
抽象类不能创建对象
抽象类除了能定义抽象方法外,其余与普通类无区别
抽象类中可以有构造方法,在创建其子类对象时自动调用
抽象类通常需要被继承,一旦有子类继承,子类就要重写抽象父类中的所有抽象方法,,或者子类也是一个抽象类
interface
用于定义接口中的关键字,代替class,能让java实现“多继承”。
如果某个抽象类中的所有方法都是抽象方法时,可以将该类改为接口。
abstract class--->interface
接口是一个完全抽象类,其中的方法都是public abstract修饰的抽象方法,其中的属性都是public final static修饰的静态常量。
接口中没有构造方法,不能创建对象
接口通过implement"继承"。类A implements 接口A,接口C,称为类A实现了接口A和接口B,一个类可以继承多个父接口
一个类一旦实现了某个接口,就要重写其中的所有抽象方法
JDK1.8后,可以在接口中定义fefault或static修饰的方法,该方法不用重写
static
被static修饰的内容成为静态成员
静态成员在类加载时就会保存到内存中,所以访问时通过类名直接访问
当某个属性或方法被高度重用时,将其定义为静态的,之后通过类名方便调用
修饰方法
public static void fun(){
}
修饰属性
public static final int NUM=123;
定义代码块
static{
//在调用该类中的方法或创建对象时自动执行一次
}
可变参数
当某个方法的参数是同一种类型且数量未知时,参数列表中可以使用课表参数定义
修饰符 返回值类型 方法名(数据类型...形参名){
//这时的参数的数量是可变的。[0,∞)
//整个参数是一个数组
}
特点
可变参数只能出现一次,且是最后一个参数
可变参数实际是一个数组
调用参数为可变参数的方法时,传递的实参要使用逗号隔开
public class Main{
/*
计算一些数字的综合
*/
public int getSum(int...nums){
int sum=0;
for(int num:nums){
sum+=num;
}
return sum;
}
public static void main(String[] args){
Main main=new Main;
//调用可变参数的方法,参数之间使用逗号隔开
System.out.println(main.getSum(1,2,3,4);
}
}
枚举
java中的枚举是一个特殊的类,是一些常量的集合。
如星期可以用数字1-7表示,也可以用“周一到周天”表示。
也可以用SUN,MON,TUE,WED,THU,FRI,SAT这些来表示。
这三种都可以称为枚举,对星期这个概念进行枚举。
定义枚举类型
public enum 枚举类型名{
常量1,常量2...常量N
}
使用枚举类型
public class Main{
public static void main(String[) args{
//遍历自定义的枚举类型
//for (week value: week.values()){
// System.out.println(value);
//}
System.out,println("请输入星期的简写:");
Scanner scan=new Scanner(System.in);
String input=scan.next();
//根据字符串获取对应的枚举类型
week week=week.valueOf(input);
//判断枚举类型
switch(week){
case SUN:
System.out.println("星期天");
break;
case MON:
System.out.println("星期一");
break;
case TUE:
System.out.println("星期二");
break;
case WED:
System.out.println("星期三");
break;
case THU:
System.out.println("星期四");
break;
case FRI:
System.out.println("星期五");
break;
case SAT:
System.out.println("星期六");
break;
default:
System.out.println("单词有误");
}
}
}
内部类
内部类,是指定义在类中的类。
内部类通常使用private修饰,定义在类中,用于隐藏类的实现细节,将其进行封装
public class outer{
//内部类通常private私有化,只能当前外部类中才能使用
private class Inner{
//这就是一个内部类
//这里可以定义属性和方法
}
}
匿名内部类
没有名称的内部类称为匿名内部类
当某个方法的参数为接口或抽象类对象时,通常会现先定义接口的实现或抽象类的子类,再创建子类作为方法的参数。
如果使用匿名内部类,就可以不用创建子类,而是直接通过匿名内部类作为参数使用。
使用案例
如USB接口
//该接口中只有一个方法
public interface USB{
void start();
}
public class Computer{
/*
当前方法的参数作为接口类型对象
*/
public void powerOn(USB usb){
usb.start();
}
}
如果要通过Computer对象调用powerOn()方法,就需要一个USB接口类型的实现类创建一个USB实现类——鼠标类
/*
USB的实现类,重写抽象方法
*/
public class Mouse implements USB{
@override
pulic void start(){
System.out.println("鼠标接入");
}
}
这时调用Computer对象调用powerOn()方法
public class Main{
punlic static void main(String[] args){
Computer com=new Computer();
USB useb=new Mouse();
com.powerOn(usb);
}
}
当使用匿名内部类,就无序创建Mouse类
public class Main{
public static void main(String[] args{
Computer com=new Computer();
//不用创建USB的实现类,使用匿名内部类作为参数
//当前powerOn()小括号中的所有内容就是一个匿名内部类
com.powerOn(new USB(){
@override
public void start(){
System.out.println("usb设备接入");
}
}
)
}
Lambda表达式
(参数类型 参数名)->{代码语句}
小括号部分表示参数列表
->表示要执行什么
{}部分表示执行的内容
使用lambda表达式遍历集合
public class Main{
public staric void main(String[] args){
String[] names={"张敏","王海","赵玉梅","白玉文"};
//将数组转换为集合
List<String>nameList=Arrays.aList(names);
//集合的for.Each()方法,参数为Consumer,是一个接口,这里不创建接口实现类,而是使用匿名内部类
nameList.forEach(new Comsumer<String>(){
/*
accept方法表示接受集合中的所有元素,方法参数就是每个元素
*/s
public void accept(String s){
System.out.println(s);
}
}
//简化1
nameList.forEach(String s)->{
System.out.println(s);
}
//简化只有一个参数
nameList.forEach(s)->{
System.out.println(s);
}
//简化只有一个参数,且忽略类型,可以省略小括号
nameList.forEach(s->{
System.out.prinln(s);
//则执行内容只有一句话,可以省略大括号
nemeList.forEach(s->System.out.println(S);
}
//最总简化版
nameList.forEach(System.out::println);
}
}