抽象类
抽象类:如果一个类中,有抽象方法的话,这个类就必须定义为抽象类。
关键字:abstract
abstract class 类名{}
抽象方法:和定义成员方法一样,只是没有方法体,没有{}。
格式:权限修饰符 abstract 返回值类型 方法名(参数列表)。
抽象类的本质:
强制子类必须做的事情,要重写抽象类的所有重写方法。
抽象类的特点:抽象类不能实例化(不能创建对象)。
注意事项:
1)抽象类不能实例化,需要具体的子类实例化。
2)如果抽象类的子类是抽象类,那么也不能实例化,但是一定存在最具体的子类。
3)抽象类的实例化-通过抽象类多态,父类引用指向子类对象,有最具体的子类。
Fu fu=new Zi();Fu是一个抽象类型
抽象类的成员特点:
1)成员变量:可以是变量,也可以是被final修饰的自定义常量。
2)成员方法:可以是抽象方法,也可以是非抽象方法。
3)构造方法:自继承父类存在继承关系,分层初始化,先让父类初始化,再让子类初始化。
abstract修饰成员方法是的注意事项:
1)abstract不能和private关键字使用,因为被private修饰的只能在当前类访问,加入abstract需要让子实现类实现这个方法,已经超出了当前的访问范围,两者起冲突。
2)abstract不能和final一块使用,因为被final修饰的成员方法不能被重写,而abstract修饰的抽象类方法需要让子实现类强制重写,有冲突。
abstract不能和static一块使用,因为被static修饰的方法,根类相关的,随着类的加载而加载,而抽象方法需要被子类重写,最终需要使用对象来抽象类多态:Fu fu=new Zi();和static一块使用抽象方法在父类中又没有方法体,加载进内存,没意义。
接口
接口:接口是一种规范(官方),就是能够实现接口中的额外的功能,那么就说明当前这个事物局部这个功能!就是体现事物能够实现的扩展的额外功能! (宏观角度)
关键字:interface
格式:interface 接口名(接口名和类名都是用“大驼峰命名法”命名){
}
接口的方法:只能是抽象方法,不能有方法体。
接口特点:不能实例化(不能创建对象)。只能通过接口多态进行实例化。
接口的子实现类和接口的关系--->implements 实现关系
格式:
interface 接口名{}
class 子实现类名 implement 接口1,接口2...{}
接口名 对象名=new 子实现类名();
如果当前接口子实现类是一个抽象类,一定会存在抽象类的子类,否则接口多态,子实现类new不了,所以当前抽象类一定有具体的子类!
接口中的成员特点:成员变量只能是常量,存在默认修饰符 public static final :可以省略不写
接口中的构造方法:没有构造方法,只是提供一些抽象方法,让子实现类实现这些方法!
接口中的成员方法:只能是抽象方法(一般都是抽象方法),存在默认修饰符:public abstract :可以省略不写
类与类的关系:继承关系,extend,只支持单继承,不支持多继承。(但是可以多继承)
类与接口的关系:实现关系,而且一个类继承自另一个类的同时,可以实现多个接口。
接口和接口的关系:继承关系,不仅可以单继承,多继承,也可以多层继承。
使用接口或者抽象类编写程序实现显示员工基本信息。具体要求如下:
(1 )使用接口或者抽象类实现基类Employer (体会接口和抽象类的不同),
包含姓名、部门和工资三个属性,显示工资的方法showSalary()和显示奖金的抽象方法showBonus()
;提示:因每位职工奖金不同,showBonus()方法定义为抽象方法,只抽象定义,不具体实现;
(2 )定义BasicEmployee 和GoodEmployee 类,重写Employer 类中的方法,不同员工有不同的工资和奖金;
(3 )定义主类进行测试,要求输入两个不同的员工信息,并输出其个人信息。
输入输出说明:
输入:
张三 前台 5000
李四 开发 6000 4000
输出:
我叫张三, 在前台部门,我的工资是5000.0
我是普通员工,没有奖金,加油升级!
我叫李四, 在开发部门,我的工资是6000.0
我是优秀员工,我的奖金是4000.0
定义接口
public interface Salary {
public void show ( ) ;
}
定义Employer 类
public class Employer {
private String name;
private String department;
private double salary;
private double bonus;
public Employer ( ) {
}
public Employer ( String name, String department, double salary, double bonus) {
this . name = name;
this . department = department;
this . salary = salary;
this . bonus = bonus;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getDepartment ( ) {
return department;
}
public void setDepartment ( String department) {
this . department = department;
}
public double getSalary ( ) {
return salary;
}
public void setSalary ( double salary) {
this . salary = salary;
}
public double getBonus ( ) {
return bonus;
}
public void setBonus ( double bonus) {
this . bonus = bonus;
}
}
定义GoodEmployee 类
public class GoodEmployee extends Employer implements Salary {
public GoodEmployee ( ) {
}
public GoodEmployee ( String name, String department, double salary, double bonus) {
super ( name, department, salary, bonus) ;
}
@Override
public void show ( ) {
System . out. println ( "我是优秀员工,我的奖金是4000.0" ) ;
}
}
定义BasicEmployee 类
public class BasicEmployee extends Employer implements Salary {
public BasicEmployee ( ) {
}
public BasicEmployee ( String name, String department, double salary, double bonus) {
super ( name, department, salary, bonus) ;
}
@Override
public void show ( ) {
System . out. println ( "我是普通员工,没有奖金,加油升级" ) ;
}
}
测试类
public class Test {
public static void main ( String [ ] args) {
Employer e= new Employer ( "张三" , "前台" , 4000.0 , 0 ) ;
System . out. println ( "我叫" + e. getName ( ) + "在" + e. getDepartment ( ) + "部门" + "工资" + e. getSalary ( ) ) ;
Salary s= new BasicEmployee ( ) ;
s. show ( ) ;
Employer e1= new Employer ( ) {
} ;
e1. setName ( "李四" ) ;
e1. setDepartment ( "开发" ) ;
e1. setSalary ( 6000.0 ) ;
System . out. println ( "我叫" + e1. getName ( ) + "在" + e1. getDepartment ( ) + "部门" + "工资" + e1. getSalary ( ) ) ;
Salary s1= new GoodEmployee ( ) ;
s1. show ( ) ;
}
}
形式参数
形式参数是基本类型,实际参数就是当前对应的数据值,并且形式参数的改变不会影响实际参数。
如果方法的形式参数是引用类型:
具体类:实际参数传递应该创建当前具体类对象;
抽象类:实际参数传递应该创建当前类的子类对象(抽象类多态)
数组:实际参数需要传递数组对象。
内部类以及返回值问题
内部类
内部类:一个类A 中定义一个类B ,将类B 就称为类A 的内部类。(类A 就是类B 的外部类)
1 )成员内部类:在外部类的成员位置定义的类
2 )局部内部类:在外部类的成员方法中定义的类
(成员内部类中的成员访问外部类的成员变量包括私有。)
class Outer { 外部类
private int num= 10 ;
class Inner { 成员内部类
public void show ( ) { 内部类的成员方法
System . out. println ( num) ;
}
}
}
public void method ( ) {
Inner inner = new Inner ( ) ;
inner. show ( ) ;
}
测试类
public class InnerClassDemo {
public static void main ( String [ ] args) {
Outer outer = new Outer ( ) ;
outer. method ( ) ;
}
}
访问内部类的方式:
将外部类的成员内部类当做是外部类的成员
直接访问方式:
前提条件:当前的成员内部类是非静态的。
格式:外部类名. 内部类名 对象名= new 外部类对象. 内部类对象;
内部类修饰符号:
1 )private ,保证数据安全性;
2 )static ,如果成员内部类都是静态的,那么成员内部类的这个方法是否静态, 已经无关了, 因为访问外部类成员只能是静态的;
面试题:
局部内部类访问局部变量的时候, 能访问吗? 局部变量有什么要求?
jdk7或者jdk7以前, 局部变量必须显示加入final 修饰, 否则访问报错
而jdk8已经jvm优化了, 此时这个num2就是常量!
使用反编译工具查看内部类的结构,发现其实已经加入了final , 为什么?
局部变量的生命周期是随着的方法的调用而存在, 随着方法的调用结束而消失; 而当前这个方法结束之后, num2局部变量也应该就不存在了, 但是我们还在使用内部类对象访问它里面这个成员方法, 而对象不会立即被GC立即回收, 等待空闲的时候回收没有更多引用的对象, 所以此时这些变量应该都是常驻内存, 使用final 定义-- -- -> 常量!
匿名内部类
匿名内部类:没有名字的类。
一般情况很少使用具体类的匿名内部类,因为具体类本身就可以new 对象。
应用场景:抽象类和接口类使用最多。
格式:
new 类名(一般情况下:都是抽象类)或者接口名(){
重新抽象类或者接口的抽象方法(){
……
}
} ;
本质:就是继承了该抽象类或者实现了接口的子类对象。
接口
interface Inter {
void show ( ) ;
void show2 ( ) ;
}
class Outer {
public void method ( ) {
Inter i = new Inter ( ) {
@Override
public void show ( ) {
System . out. println ( "show Inter..." ) ;
}
@Override
public void show2 ( ) {
System . out. println ( "show2 Inter..." ) ;
}
} ;
i. show ( ) ;
i. show2 ( ) ;
}
}
public class InnerClassTest {
public static void main ( String [ ] args) {
Outer outer = new Outer ( ) ;
outer. method ( ) ;
}
}
jdk8有一个新特性: 拉姆达表达式
当接口中有且仅有一个抽象方法的时候, 那么这个接口可以称为"函数式接口"
函数式接口-- -- > 实现方法的时候-- -- > 有一个 -> 箭头直接可以实现接口的方法
interface Love {
void love ( ) ;
}
class LoveDemo {
public void show ( Love l) {
l. love ( ) ;
}
}
class LoveImpl implements Love {
@Override
public void love ( ) {
System . out. println ( "爱生活,爱学习。" ) ;
}
}
public class InnerClassTest2 {
public static void main ( String [ ] args) {
ld. show (
( ) -> {
System . out. println ( "爱生活,爱Java,爱高圆圆" ) ;
}
) ;
}
}
返回值
方法的返回值要么是基本数据类型, 要么就是引用数据类型。
返回值是应用类型:
具体类:需要返回当前类的具体对象。
Student 类
class Student {
public void study(){
System . out. println("wo ai xue xi" );
}
}
StudentDemo 类
class StudentDemo {
public Student method ( ) {
方式一: 对象名 = new 类名( ) ;
方式二:匿名对象
return new Student ( ) ;
}
}
抽象类:需要返回当前抽象类的子类对象。
老师类
abstract class Teacher {
public abstract void teach ( ) ;
}
class TeacherDemo {
public Teacher function ( ) {
return new EnglishTeacher ( ) ;
}
}
抽象类的子类
class EnglishTeacher extends Teacher {
重写teach方法
@Override
public void teach ( ) {
System . out. println ( "英语老师教英语..." ) ;
}
}
测试类
public class ReturnDemo2 {
public static void main ( String [ ] args) {
TeacherDemo td = new TeacherDemo ( ) ;
Teacher teacher = td. function ( ) ;
teacher. teach ( ) ;
}
}
接口类:需要返回接口的子实现类对象。
接口类
interface Play {
void play();
}
接口子实现类
class PlayDemo {
public play show ( ) {
方式一:接口多态
Play play = new PlayImpl ( ) ;
return play;
方式二:匿名对象
return new PlayImpl ( ) ;
}
}
子实现类
class PlayImpl implements Play {
重写play方法
@Override
public void play ( ) {
System . out. println ( "玩LOL" ) ;
}
}
测试类
public class Test {
public static void main ( String [ ] args) {
访问PlayDemo 中的show ( ) 方法
PlayDemo pd= new PlayDemo ( ) ;
Play p= pd. show ( ) ;
play. p ( ) ;
}
}
Object类
Object : 每个类都有Object 作为超类(父类的意思),所有的类都默认继承自Object 。
常用方法:public final Class getClass ( ) ( 重要) : 获取当前正在运行的Class 类对象( 字节码文件对象)
public int hashCode ( ) : "一个地址值" , 不是实际意义的地址值, 它是经过底层的哈希算法。
public native int hashCode ( ) ; 没有方法体 -- 关键字native 。
被native 修饰的方法:Java Native Interface : Java 本地接口。
public String toString ( ) ,返回对象的字符串表示形式, 结果应该是一个简明扼要的表达,容易让人阅读。 建议所有子类覆盖此方法。
public boolean equals ( Object obj) : 比较对象是否相等,引用该类型比较的是地址值是否相同
public boolean equals ( Object obj) : 比较对象是否相等,引用该类型比较的是地址值是否相同。
== 和equals的区别:
== : 如果连接都是两个基本数据类型: 比如int 比较的是数据值是否相等,如果连接的是引用类型, 那么比较的是地址值是否相同。
equals ( ) 是Object 类的方法
源码
public boolean equals ( Object obj) {
return ( this == obj) ;
}
如果我们不重写Object 的equals方法, 默认比较的是两个引用类型的地址值是否相同, 如果重写了equals方法而且同时重写了hashCode ( ) 比较的是成员信息是否相同! 所以几乎所有类都会覆盖! 论何时覆盖该方法equals ( ) ,通常需要覆盖hashCode方法。
例:
String s1 = "hello" ;
String s2 = "world" ;
String s3 = "helloworld" ;
System . out. println ( s3 == ( s1+ s2) ) ;
System . out. println ( s3. equals ( s1+ s2) ) ;
System . out. println ( s3 == "hello" + "world" ) ;
System . out. println ( s3. equals ( "hello" + "world" ) ) ;
答:false true true true s1与s2相加是先在字符串常量池中开一个空间,然后拼接,这个空间的地址就是s1与s2拼接后的地址。与s3的地址不同,所以输出为false 。
S3与”hello”+ ”world”相比较,”hello”+ ”world”先拼接为”helloworld”再去字符串常量池中找是否有”helloworld”,与s3共用一个字符串对象,所以true 。最后一个为equals,只比较值,值相等就为true 。
Object类的方法
Object 里面的clone方法
本质是复制对象(浅克隆)
protected native Object clone ( ) throws CloneNotSupportedException ;
本地方法-- -- > 非Java 语言实现的, 通过系统资源调用的
如果此对象的类不实现接口Cloneable ,就没有成员方法,就是标记接口。如果某个对象的类实现这个接口的话, 那么就说明这个类可以使用Object 的clone来进行对象的复制!
则抛出CloneNotSupportedException 。
public class ObjectDemo {
public static void main ( String [ ] args)
throws CloneNotSupportedException {
Student s1 = new Student ( "张三" , 20 ) ;
Student s2 = new Student ( "张三" , 20 ) ;
Object obj = s1. clone ( ) ;
Student s3 = ( Student ) obj;
System . out. println ( s3) ;
Student s4 = s1 ;
System . out. println ( s4) ;
}
}
String
Scanner
Scanner类提供一些判断功能:
public boolean hasNextXXX(){} XXX就对应的数据类型
判断下一个录入的数据是否为XXX类型
boolean hasNextInt() :判断是否下一个录入的是int类型
boolean hasNextLine(); 判断是否下一个录入的是String类型
获取功能:一直在用
XXX nextXXX() --->录入数据
int nextInt() {} 录入的是int类型
String nextLine(){} 录入的是String类型
键盘录入的细节
先录入的String,在录入int,没问题
先录入int,在录入String,有问题
就是因为在录入数据的时候,接收数据--->按 "回车键",下一个录入的是String
就是因为"回车符号"---->就是换行---->当空字符进去了,所以第二个数还没有录入的就完了
解决方案:
1)String nextLine() 录入一行数据,--->官方用法
使用另一种方法:public String next() 录入的指定的字符串内容(通用格式)
2)就要使用nextLine()方法录入String,在录入String之前,重新创建一个新的键盘录入对象,也可以!
String类型使用时最多的-->所以以后如果有int类型的录入---->
可以都是用String数据接收(nextLine())--->
然后将String-->Integer->int(前提---->录入的"数字字符串")
String类
String的构造方法
String() :空参构造
String(byte[] bytes) :有参构造,里面是字节数组(将byte[]--String)
解码过程(将看不懂的内容转化成字符,平台默认字符集现在"utf-8",所以不用带字符集!)
String(byte[] bytes, int offset, int length) :将一部分字节数组--->字符串
参数1:字节数组
参数2:指定的位置开始
参数3:指定的长度
String(char[] value) :将字符数组转化成字符串
String(char[] value, int offset, int count) :将一部分字符数组--->字符串
参数1:字符数组对象
参数2:从指定位置开始
参数3:记录数多少个
String(String original) :构造的字符串对象,参数为字符串常量值
""空字符序列和null不同!
"":说明有内容,空的,依然可以使用String类型里面的所有功能
null:空对象,不能在使用String的方法,在使用,就空指针异常NullPointerException
String类的常用判断功能
public boolean contains(String序列 s):字符串中是否包含指定的字符
public boolean endsWith(String suffix):是否以指定的字符串结尾
public boolean startsWith(String prefix):是否以指定的字符串开头
public boolean isEmpty()判断字符串内容是否空字符,长度为0,就是true
public boolean equals(Object anObject)-->重写Object,区分大小写判断字符串内容是否相同
public boolean equals(Object anObject):不区分大小写判断字符串内容是否相同
String类的获取功能
public char charAt(int index)获取指定索引出的字符值
public String concat(String str):拼接功能--->获取新的字符串
最传统的拼接使用+:字符串拼接符号
public int indexOf(int ch)返回指定字符第一次出现的字符串内的索引
public int indexOf(String str):返回指定字符串中子字符串第一次出现索引值
public int lastIndexOf(int ch):返回指定字符最后一次出现的索引
public int lastIndexOf(String str)
String substring(int beginIndex) :从指定位置开始默认截取到末尾--->返回一个新的字符串
String substring(int beginIndex, int endIndex) :从指定位置开始截取到指定位置结束
包前,不包后(只能取到endIndex-1处),返回的一个字符串
public String[] split(String regex):按照指定的分割符号--拆分成字符串数组
String类的转换功能
public char[] toCharArray():将字符串转换为字符数组
public byte[] getBytes():将字符串转换成字节数组---使用平台默认字符集编码
String的构造方法---String(byte[] bytes):使用平台默认字符集解码
public String toLowerCase():将字符串转换成小写
public String toUpperCase():将字符串转换成大写
万能方法:将任何数据类型转换成String
public static String valueOf(int/double/float/char[]/Object i)
String类的其他功能
public String replace(char oldChar,char newChar)将指定的字符使用新字符进行替换
public String replace(String oldStr,String newStr)替换指定字符串
StringBuffer
StringBuffer:字符串缓冲区,里面都存储的可变的字符序列,但是它的类型StringBuffer类型
构造方法:
StringBuffer() 空参构造
StringBuffer(String str):有参构造
中文很特殊--->超过某个容量值,系统会重写分配
StringBuffer(int capacity):指定容量大小(很少用,一般使用第一个和第二个构造方法)
获取功能:
int length():获取字符串缓冲区的长度(里面实际的字符序列的长度)
public int capacity():获取缓冲区的容量大小
字符串缓冲区默认16个初始容器里(英文字符),
如果里面有字符序列
构造方法就是前两个使用居多,第一个是最常用的,空参构造方法--->创建字符串缓冲区对象!
StringBuffer的添加功能
public StringBuffer append(任何类型数据) :追加功能 (常用的),返回值是字符串缓冲区本身,在缓冲区中的默认的字符序列后面依次追加。
public StringBuffer insert(int offset,char c/String str):返回值是字符串缓冲区本身,在指定的位置插入指定的字符或者字符串
StringBuffer的反转功能
StringBuffer reverse() 返回字符串缓冲区本身
截取功能和String类型的截取功能一样,
String subString(int beginIndex):从指定位置默认截取到末尾--->String
String subString(int beginIndex,int endIndex)--->从指定开始
截取到指定位置结束endIndex-1出,返回的新的字符串
String和StringBuffer的区别
String是一个特殊的引用类型,形参的改变不会实际参数,效果和基本类型一致!
String是常量,一旦被赋值,其值不能被更改!
StringBuffer:是线程安全的一个类,支持可变的字符序列,
引用类型除过String之外,形参改变直接影响实际参数!
Integer类
Integer类---包含int类型的值
还提供了一些将int转换为String和String转换为int
byte --- Byte
short--- Short
int --- Integer
long --- Long
float--- Float
double --- Double
char --- Character
boolean --- Boolean
提供些包装类型就是为了更好的String类型之间相互相互
Integer---->parsetInt(String str)---->int
String--->int
Integer的静态功能parseInt(String str)--->int
String --->long
Long的方法
parserLong(String str)--->long
parseXXX方法(String str)--->xx基本类型
Integer类的有些成员变量
public static final int MAX_VALUE ;自定义常量
public static final int MIN_VALUE ;
静态方法
public static String toBinaryString(int i):将十进制数据--->二进制数据的字符串形式
public static String toOctalString(int i):将十进制数据--->八进制字符串形式
public static String toHexString(int i):将十进制数据---->十六进制数据的字符串形式
Integer的构造方法
Integer作为中间桥梁,,可以实现int类型和String类型的相互转换
同理,其他的基本类型和String类型的相互转换--使用基本类型对应的包装类类型
(实际开发中,int和long,double使用最多,)
Integer的内部缓存区特点IntegerCache
Integer类的构造方法
Integer ( int value) 可以将基本类型数据转换为Integer 类。
Integer ( String s) throws NumberFormatException : 将String 转换为Integer 。
形式参数必须为"数字字符串" , 否则如果不是数字字符串,NumberFormatException 出现数字格式化异常。
从jdk5以后有自动拆装箱,增强for 循环,泛型,枚举,静态导入,可变参数……
自动拆装箱:
包装类类型会自动降为基本类型-- -> 称为"拆箱"
基本类型会提示为对应的包装类型-- -> 称为"装箱"
int 类型转化为Integer 类型
import java. io. PrintStream ;
public class IntegerDemo
{
public IntegerDemo ( )
{
}
public static void main ( String args[ ] )
{
int x = 100 ;
Integer i = new Integer ( x) ;
i = Integer . valueOf ( i. intValue ( ) + 200 ) ;
System . out. println ( i) ;
}
}
int 类型和String 类型的相互转换
方式1 : 最接的方式 , 空串拼接。
方式2 : int 类型先转化为Integer 类型,然后再转化为String 类型。
Character类型
Character : char 类型保证类型
构造方法:Character ( char value) 包含 char 类型的值
public class CharacterDemo {
public static void main ( String [ ] args) {
Character character = new Character ( 'a' ) ;
System . out. println ( character) ;
}
}
集合
集合整体框架体系:
父接口Collection<E> (单列集合,里面只能存储一种引用类型)
子接口List<E> ArrayList<E> LinkedList<E> Vector<E>
Collection:是所有集合的根接口,集合表示一组被称为其元素的对象。
一些集合允许重复元素(List集合),而其他集合不允许重复(Set:唯一的),
JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如Set和List--->实现类(接口多态!)
Collection集合的功能
添加:boolean add(E--Object(任意类型元素) e)
判断:
boolean contains(Object o):是否包含指定的元素
boolean equals(Object o):比较
boolean isEmpty():判断集合是否为空
删除:
void clear():删除所有元素
boolean remove(Object o):删除集合中指定的元素
获取功能:
int size() 获取集合元素数
Iterator<E> iterator() :迭代器
jdk5以后提供了新特性增强for循环语句代替迭代器,写法更简单!
Object[] toArray():传统 方式集合转换成对象数组--->遍历
Collection 的成员方法
Iterator < E > iterator ( ) 迭代器
public class CollectionDemo {
public static void main ( String [ ] args) {
Collection < String > c = new ArrayList < > ( ) ;
c. add ( "hello" ) ;
c. add ( "world" ) ;
c. add ( "JavaEE" ) ;
Iterator < String > it = c. iterator ( ) ;
while ( it. hasNext ( ) ) {
String s = it. next ( ) ;
System . out. println ( s+ "----" + s. length ( ) ) ;
}
}
}
list集合
特点:有序集合,存储元素和取出元素一致!允许元素重复
可以使用Collection集合的迭代器来遍历集合。
List集合的特有的遍历方式:
E(指定类型) get(int index):获取指定位置处的元素的+size()获取集合的元素数的,使用普通for循环。
还可以使用增强for循环。
列表迭代器
ListIterator<E> listIterator()
ListIterator接口
正向遍历
boolean hasNext():判断是否有更多下一个元素可以迭代(遍历)
E next()获取下一个可以遍历元素
反向遍历
boolean hasPrevious():判断是否有更多的上一个元素可以迭代(遍历)
E previous():获取上一个上一个可以遍历的元素
反向遍历不能单独使用,必须先有正向遍历。
public Object remove(int index):通过角标删除对应的元素!