作为Java开发:应该站在分类学家的角度看问题
- 植物类
- 动物类
- 哺乳类
- 爬行类
- 有足类(蜥蜴)
- 无足类(蛇)
- 非哺乳类
类:具有相同特征的事物,抽象出来的概念(直观点说就是想出来的)
对象:现实世界中客观存在的事物(比如:张三,李四 都是现实存在的人)
面试题:类和对象是什么关系?
答案:
类是对象的模板
,因为只有创建好类,才可以通过类创建不同的实例(对象)
对象是类的实例
,可以通过一个类创建无数个实例
类:具有相同特征的事物抽象出来的
静态特征(属性):性别 身份证号…
动态特征(方法):学习 吃饭 打游戏…
public class 类名{
属性:性别 身份证号
方法:学习 吃饭 打游戏
}
属性语法:
//访问修饰符 数据类型 属性名:
public String name;
方法语法:
//访问修饰符 返回值类型 方法名称(参数){
//方法实现内容
//}
比如:
public static void main(String[] args){
}
访问修饰符:是Java为了控制属性和方法的访问权限
面试题:四种访问修饰符的使用区别
访问修饰符 | public(公开的) | protected(受保护的) | 默认(省略) | private(私有的) |
---|---|---|---|---|
本类 | √ | √ | √ | √ |
同包 | √ | √ | √ | |
子类 | √ | √ | ||
其他 | √ |
返回值类型:表示方法在运行后需要的结果
通常:必须添加return表示对应的返回结果
特殊:void表示不需要返回,可以不写return结果的
public String test(){ String name="100"; return name; } //特殊情况 //因为A是B的父类 B的类型也属于A的类型 public A test2(){ A a=new A(); B b=new B(); return b } class A{ } //B继承了A类 class B extends A{ }
参数:
形式参数(形参):在方法定义的时候写入的参数
//abc就是形参,一般形参需要写数据类型和形参名
public void test(String abc)
实际参数(实参):在方法调用的时候传入实际的值
//name就是实参 实参名称和形参名称不需要一样,但是类型必须一样
String name="张三";
test(name);
如何使用类中的属性和方法?
//数组使用
int[] nums=new int[5];
nums.length
//1.创建对象(需要先有类)
类名 对象名=new 类名();
Scanner sc = new scanner(System.in);
Dog d = new Dog();
//2.通过对象名.属性 调用属性 或者给属性赋值
String s=d.name
d.name="大黄";
//3.通过对象名.方法(实参)
d.eat("骨头");
面试题:成员变量和局部变量的区别是什么?
- 作用范围不同:成员变量可以在全类中使用,局部变量在哪里声明的在哪里使用
- 优先级不同:如果局部变量和成员变量同时存在,局部变量优先于成员变量(就近原则)但是可以通过this关键字直接调用成员变量
- 默认值不同:成员变量会有默认值,所以不需要赋值也可以使用,局部变量必须先赋值才可以使用因为它没有默认值
面试题:char类型有几种赋值方式?
答案:1.可以通过单引号赋值单个字符
2.可以赋值ASCII码,ASCII只是表示计算机键盘中的按键 65-90是A-Z 97-122是a-z
3.可以赋值Unicode编码,可以涵盖计算机所出现的所有字符,一共65535个,语法:\u4567,这里包 含中文,所以char类型也可以存储中文
构造方法:也叫构造器,也叫构造函数,是Java中一种特殊的方法,因为构造方法没有返回值
这个概念,正常创建对象时,都需要依赖这个方法,如果类中没有定义构造方法,也会存在一个默认的无参构造方法
不能定义返回值(void都不能写)
构造方法名称必须和类名一样
构造方法可以提供多个,而且存在默认的无参构造
public class People{
//无参构造方法
public People(){
name="未知";
age=0;
}
//带参数的构造方法
public People(String name,int age){
this.name=name;
this.age=age;
}
//构造方法可以提供多个,前提是参数列表一定要不同
//参数类型 参数个数 参数顺序
public People(int a){
}
}
//构造方法:
//1.用于创建对象 new 对象() 就是调用构造方法的过程
//2.一般用于给属性做初始化
public class Student {
String name;
String sex;
int age;
public void print(){
System.out.println("我是: " + name+",我今年"+age+"岁"+",性别:"+sex);
}
//默认会存在一个无参构造,但是如果手动编写,默认的就会失效
public Student(){
System.out.println("执行了无参构造方法");
}
//构造方法可以提供多个(前提是参数列表不同,类型,个数,顺序)
public Student(String name){
System.out.println("执行了有参构造方法");
}
public Student(String a,int b){
}
public Student(int a,String b){
}
public Student(int a,int b){
}
public Student(String name,String sex,int age){
this.name = name;
this.age = age;
this.sex=sex;
}
//构造方法为什么可以提供多个,因为Java支持方法重载
//方法重载:
}
class TestStudent{
public static void main(String[] args) {
Student stu = new Student("张三");
Student stu2 = new Student(10,"bb");
Student stu3 = new Student(10,20);
Student stu4 = new Student("10",20);
System.out.println(stu);
//Student s=new Student();
//s.name="张三";
//s.sex="男";
//s.age=10;
//s.print();
Student s=new Student("张三","男",18);
s.print();
}
}
一般情况下发生在同一个类中,要求方法名一样,参数列表不同(个数,顺序,类型),跟返回值无关
public class Dog {
//重载:一个类中方法名相同,参数不同,返回值无关
public void play(){
}
public int play(String msg){
return 0;
}
//还可以传递可变长的参数 语法:类型 ... 参数名
//以后 传递0-多个参数 都可以通过可变长参数来处理
//形成一个数组参数
public void play(String ... s){
for(String args:s){
System.out.println(args);
}
}
public static void main(String[] args) {
Dog d = new Dog();
d.play("aaa");
d.play();
d.play("a","b","c","d");
//jdbc 肯定要关闭资源 但是资源如果不确定 就可以通过可变长参数处理
//close(ResultSet,PreparedStatement, Connection)
//close(PreparedStatement, Connection)
}
}
this表示当前类的对象
用法1:this.属性名,调用类中的成员变量 —常用
用法2:this.方法(),调用类中的普通方法(静态方法不可以)
用法3:this() 和 this(参数) 调用本类无参和有参构造方法
特殊:this()和this(参数)必须写在第一行代码,并且只能用在构造方法中
//this关键字
public class User {
int id;//用户编号
String username;//账号
String password;//密码
double money;//存款
public User() {
//必须写在构造方法第一行代码,否则报错
//不要循环调用
this(15.5);
System.out.println("调用无参构造");
//this(15.5) 错误的
}
public User(double money){
//给属性赋初值
this.money = money;
//用法偏少
this.setMoney(100);
//用法偏多
setMoney(200);
System.out.println("调用一个参数构造");
}
public User(String username, String password) {
System.out.println("调用两个参数构造");
this.username = username;
this.password = password;
}
public User(String username, String password, double money) {
this(username, password);
this.money = money;
System.out.println("调用三个参数构造");
//this.username = username;
//this.password = password;
//this.money = money;
}
//存款的方法
public void setMoney(double money) {
this.money+=money;
}
//静态方法
public static void main(String[] args) {
//this.money this不能使用在静态的方法中
new User("admin", "123456", 100.0);
}
}
面试题1:为什么普通方法不能使用this()?
答案:this() 调用无参构造方法,目的是为了创建对象,普通方法必须通过对象才可以调用,两者的目的相互冲突
面试题2:为什么(static)静态方法不能使用this?
答案:this表示当前对象,static属于类本身,随着类加载才会执行,因为先有类才有对象,等执行静态方法的时候,对象可能还没来得及创建,所以使用时机不对
static表示一个修饰符,可以修饰属性,修饰方法,修饰代码块,表示静态的意思
修饰属性:静态属性或者类属性,是属于类的不是属于对象的,无论创建多少个对象都会共享同一个静态变量,通常推荐通过类名.静态属性调用
对象名.静态属性 ---不推荐,因为它属于类
类名.静态属性 ---推荐方式,省略创建对象步骤
比如:System.in;
修饰方法:静态方法,也叫类方法,也推荐通过类名调用
注:静态方法只能使用静态变量和静态方法;普通方法:可以调用普通属性和方法 ,也可以调用静态属性和方法
修饰代码块:静态代码块,会随着类加载最先执行,而且只会执行一次,而且只能调用静态属性和静态方法
静态代码块应用场景:适合给程序属性做初始化,加载配置文件(jdbc 数据库连接文件)
代码块分类:
public class Demo3 {
{
System.out.println("构造代码块,new对象的时候执行");
}
public Demo3() {
System.out.println("构造方法");
}
public void test(){
{
System.out.println("普通代码块,方法调用的时候执行");
}
}
static {
System.out.println("静态代码块,类加载才执行,而且执行一次");
}
public static void main(String[] args) {
Demo3 d1=new Demo3();
Demo3 d2=new Demo3();
d1.test();
d1.test();
d2.test();
}
}
面试题:为什么要使用static?或者static有什么优势?
1.可以不通过对象来调用属性和方法,推荐使用类名调用
2.静态变量是属于类的,不同的对象共享同一个静态变量,有一个对象修改了,其他对象使用也会修改
3.静态变量,存储在内存中方法区(静态区),无论创建多少个对象,不用花费额外的空间再来保存,比较节省内存
封装:将类中的属性私有化(private),为了让其他类不能随便使用我的属性,还要对外提供两个公开的方法(set XX和get XX)用于设置和获取对应的属性,这样就可以根据我的业务需要设置我的属性规则,就可以防止数据非法,提高了安全性
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为
//手机类:进行封装
//手机型号(IOS,安卓,鸿蒙) 颜色 价格(0-5000)
//1.属性私有化(不让其他类随便使用我的属性)
//2.对外提供 get 和 set 获取和设置属性(用户可以在 set 和 get 设置自己的属性规则)
public class Phone {
private String type;
private String color;
private double money;
//设置手机型号的方法 setType 一般是需要带参数的
public void setType(String type){
if("IOS".equals(type) || "安卓".equals(type) || "鸿蒙".equals(type)){
this.type = type;
}else{
System.out.println("输入的手机型号不合法,默认:安卓");
this.type="安卓";
}
}
//获取手机型号的方法 getType 一般是需要带返回值的
public String getType(){
return type;
}
//设置获取颜色(银色,黑色)
//alt+insert(ins) 快捷生成 构造 get set ....
public void setColor(String color){
if("银色".equals(color) || "黑色".equals(color)){
this.color = color;
}else {
System.out.println("输入的手机颜色不合法,默认:黑色");
this.color="黑色";
}
}
public String getColor(){
return color;
}
//设置获取价格(0-5000)
public void setMoney(double money){
if (0<=money && money<=5000){
this.money = money;
}else {
System.out.println("输入的手机价格不合法,默认:5000");
this.money=5000;
}
}
public double getMoney(){
return money;
}
}
class TestPhone{
public static void main(String[] args) {
Phone p=new Phone();
p.setType("IOS");
p.setColor("银色");
p.setMoney(4000);
System.out.println(p.getType());
System.out.println(p.getColor());
System.out.println(p.getMoney());
System.out.println("----------------------");
Phone p1=new Phone();
p1.setColor("绿色");
p1.setMoney(5000);
p1.setType(null);
System.out.println(p1.getType());
System.out.println(p1.getColor());
System.out.println(p1.getMoney());
}
}
继承:将很多类共同的特征(属性和方法),提取出通过一个类完成,这样其他类就可以继承这个类,那么也会具有这些共同特征,可以防止代码冗余,使用extends关键字来继承指定的类,这个类一般称之为父类(基类),子类可以继承父类的非私有资源
注意:Java只支持单继承,一个类只能有一个直接父类,但是可以多重继承,因为父类还可以继承其他类
//继承 子类 父类
public class Cat extends Pet{
String color;
@Override
public void print(){
System.out.println("猫的基本信息:");
//子类可以使用父类的非私有资源
System.out.println(name);
System.out.println(color);
System.out.println(health);
System.out.println(love);
}//颜色
}
class Tiger extends Pet{
String type;//品种
//方法的重写:为了更好的体现出子类自己的特性
@Override//用于标注是否是重写,可以省略
public void print(){
System.out.println("老虎的基本信息:");
//子类可以使用父类的非私有资源
System.out.println(name);
System.out.println(type);
System.out.println(health);
System.out.println(love);
}
}
//宠物类:老虎类和猫类都属于宠物类的子类
class Pet{
//猫类和老虎类共同的特征(属性和方法)
String name;
int health;
int love;
public void print(){
System.out.println("宠物的基本信息:");
System.out.println(name);
System.out.println(health);
System.out.println(love);
//父类不能使用子类的资源
//System.out.println(type);
}
}
class Test{
public static void main(String[] args) {
Cat c = new Cat();
c.name="花花";
c.health=100;
c.love=50;
c.color="橘猫";
c.print();
Tiger t = new Tiger();
t.name="小王";
t.health=100;
t.love=20;
t.type="东北虎";
//调用哪个方法 需要t对象指向的是谁,这里指向的是老虎类
t.print();
}
}
方法重写Override—面试题:
1.访问修饰符:可以相同或者不能严于父类
2.返回值:可以相同或者是其子类
3.方法名:必须相同
4.参数列表:必须相同
5.不能抛出比父类更多的异常
super关键字使用方式和this关键字一样,super泛指当前父类对象
1.this.属性 this.方法
2.this() this(参数)
3.super.属性 super.方法
4.super() super(参数) 调用父类有参无参构造
注:Java类构造方法中,默认第一行会添加super()
面试题:父类静态代码块 父类构造方法 父类构造代码块 子类静态代码块 子类构造方法 子类构造代码块 他们的执行顺序是什么?
答案:父类静态代码块 --> 子类静态代码块 --> 父类构造代码块 --> 父类构造方法 -->子类构造代码块 -->子类构造方法
多态—难点:
同一个类的对象调用相同的方法时,可以有不同的表现形式,或者同一个类型,使用不同的实例(new 对象)会执行不同的操作
比如:打印机 – 黑白打印机 —>打印黑白信息
--彩色打印机 —>打印彩色信息
实现多态前提:
满足类的继承
方法的重写
父类引用指向子类对象
父类 对象1 = new 子类1();
父类 对象2 = new 子类2();
//结果:对象.方法() 就会运行子类重写后的方法
多态调用成员的特点
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:Java在编译代码的时候,会看左边的父类中有没有这个变量,
//如果有,编译成功,如果没有,编译失败
//运行也看左边:Java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a.name); //动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:Java在编译代码的时候,会看左边的父类中有没有这个方法,
//如果有,编译成功,如果没有,编译失败
//运行看右边:Java运行代码的时候,实际上运行的是子类中的方法
a.show(); //Dog-----show方法
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("Animal-----show方法");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show(){
System.out.println("Dog-----show方法");
}
}
class Cat extends Animal{
String name = "猫";
@Override
public void show(){
System.out.println("Cat-----show方法");
}
}
多态的优势
多态的弊端
方法定义和原来一样,区别在于,抽象方法只有方法的声明部分,没有方法的实现部分
//方法的声明部分
public void test(){
//方法实现部分
}
//抽象方法需要通过关键字abstract
public abstract void test();
final可以修饰类:表示这个类是最终类,表示这个类不能被继承
final可以修饰属性:表示这个属性是最终属性,说明这个属性一旦赋值不能修改,一般我们称之为常量(命名规则都要大写)
final可以修饰方法:表示这个方法是最终方法,这个方法不能被重写
//测试final修饰符
public final class TestFinal {
//方法不能被重写
public final void testMethod() {
}
//final修饰属性是常量 必须先赋值,一旦赋值不能修改
final String sex="男";
//final修饰属性 如果我不想直接赋值 怎么解决
final static double pai;
static {
pai=3.1415926;
}
// public TestFinal() {
// this.pai=3.14;
// }
}
//final修饰的类不能继承
//class Child extends TestFinal{
//
//}
class MyError extends Error{
}
面试题1:Java是否可以写一个类去继承String或者Error类
答案:因为String是final修饰的类,不能被继承,Error不是final修饰的类,可以自定义类去继承Error
面试题2:abstract和final修饰符是否可以同时使用,为什么?
答案:abstract修饰的类属于抽象类,目的就是让子类实现(重写)我的方法;final修饰的类不能被继承,方法也不能被重写,两者有冲突,所以不能一起使用
接口:相当于一个特殊的抽象类,本身就是一种规范和约定,如果想使用接口,就必须符合它的规范和约定,它里面的约定和规范就是里面的抽象方法,相比抽象类,里面的方法一般都是抽象方法
//测试接口:关键字interface
public interface MyInterface {
//特点1:接口的所有属性都是常量(默认修饰符 public static final)
public static final String name="a";
String sex="male";
//特点2:接口没有构造方法
// public MyInterface(){
//
// }
//特点3:接口的方法都是抽象方法 不能有普通方法
//默认修饰符都是 public abstract
public abstract void test();
void test2();
//public void test3(){}
//特点4:接口和接口 支持多继承 ; 类和类 单继承
// 类和接口 表示实现 支持多实现
//特点5:不能实例化
//特点6:jdk1.8 接口支持静态方法和默认方法
public static void method(){
}
//接口表示这个方法不强制要求你需要重写
public default void method2(){}
}
interface MyInterface1 extends MyInterface2,MyInterface3 {
void test1();
}
interface MyInterface2 {
void test2();
}
interface MyInterface3 {
void test3();
}
class My implements MyInterface3,MyInterface2 {
@Override
public void test3() {
}
@Override
public void test2() {
}
}
class My2 implements MyInterface1 {
@Override
public void test1() {
}
@Override
public void test2() {
}
@Override
public void test3() {
}
}
接口的所有属性都是常量(默认修饰符 public static final)
接口没有构造方法
接口的方法都是抽象方法,不能有普通方法,默认修饰符都是 public abstract
jdk1.8后,接口支持静态方法和默认方法
接口不能被实例化
接口和类之间是实现关系,通过implements关键字表示
接口的子类(实现类)要么重写接口中的所有抽象方法,要么是抽象类
接口和类之间的实现关系,可以单实现,也可以多实现,实现类还可以在继承一个类的同时实现多个接口
public calss 类名 implements 接口名1,接口名2{}
public class 类名 extends 父类 implements 接口名1,接口名2{}
解决接口与接口实现类之间的矛盾问题,当一个接口中抽象方法比较多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式
函数式接口:表示只有一个抽象方法,其他的常量默认方法除外,一般可以使用@FunctionalInterface来声明,也是jdk1.8特性
//函数式接口
@FunctionalInterface
public interface MyFunction {
public void test();
}
@FunctionalInterface
interface MyFunction2 {
public int getNum(int a, int b);
String name="java";
public default void test(){
//Consumer
//Predicate
//Comparator
}
}
//函数式接口的应用场景:适用于给lambda表达式使用
lambda表达式就是用于描述函数式接口,因为函数式接口里面有且只有一个抽象方法,而lambda也只能描述一个方法
语法规则1:
() -> {}
():就表示方法的参数列表
->:就是间隔符
{}:就表示方法的实现部分
语法规则2:(数据类型 形参名) -> {} 数据类型是可以省略的
MyFunction2 my3 = (a,b)->{...}
语法规则3:当接口的抽象方法只有一个参数的时候,数据类型和()都可以省略
//函数式接口
@FunctionalInterface
public interface MyFunction {
public void test();
}
@FunctionalInterface
interface MyFunction2 {
public int getNum(int a, int b);
String name="java";
public default void test(){
//Consumer
//Predicate
//Comparator
}
}
@FunctionalInterface
interface MyFunction3 {
public void test(int a);
}
//函数式接口的应用场景:适用于给lambda表达式使用
class TestLambda{
public static void main(String[] args) {
MyFunction my = new MyFunction(){
@Override
public void test() {
System.out.println("测试1代码");
}
};
//利用Lambda表达式 也可以实现上面的过程
MyFunction my2=() -> {
System.out.println("测试2代码");
};
my.test();
my2.test();
MyFunction2 my3 = (int a,int b) ->{
System.out.println(a+" "+b);
return a+b;
};
//可以省略形参类型
MyFunction2 my4=(a,b)->{
return 10;
};
//一个参数 还可以继续省略()
MyFunction3 my5= a ->{
};
//如果方法实现部分只有一句话 也可以省略{}
MyFunction3 my6=a-> System.out.println(a);
//如果方法实现部分有返回值,并且只有一句话,{}和return都可以省略
MyFunction2 my7 = (a,b) ->a+b;
int result = my3.getNum(2,3);
System.out.println(result);
}
}
语法规则4:不是Lambda语法,可以借助于其他类写好的静态方法来实现我自己的函数式接口方法
要求:静态方法 参数和返回值必须一样
语法:函数式接口 名称=类名::方法名;
import java.util.Arrays;
public class TestSortLambda {
public static void main(String[] args) {
//借助于Arrays.sort() 来实现我的函数式接口
//要求:参数和返回值一致
MySort my= Arrays::sort;
int[] nums={3,1,5,4,2};
my.mySort(nums);
System.out.println(Arrays.toString(nums));
}
}
interface MySort{
public void mySort(int[] a);
}
class MyTest{
public static String concat(String a, String b){
return a+b;
}
public static void main(String[] args) {
MyString my=MyTest::concat;
String result=my.XXX("java","script");
System.out.println(result);
}
}
interface MyString{
String XXX(String a, String b);
}
注意:Lambda表达式只能简化函数式接口的匿名内部类的写法
在一个类里面,再定义一个类,内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义,比如:汽车的发动机,ArrayList的迭代器,人的心脏等等
匿名内部类本质上就是隐藏了名字的内部类
new 类名或者接口名() {
重写方法
};
//例如
new Inter(){
public void show(){
}
};