精华内容
下载资源
问答
  • 深入理解Java String

    万次阅读 多人点赞 2018-06-28 21:15:55
    Java语言了中,所有类似“ABC”的字面值,都是String的实例;String位于java.lang包下,是Java语言的核心,提供了字符串的比较、查找、截取、大小写转换等操作;Java语言为“+”连接符(字符串连接符)以及...

    在Java语言中,所有类似“ABC”的字面值,都是String类的实例;String类位于java.lang包下,是Java语言的核心类,提供了字符串的比较、查找、截取、大小写转换等操作;Java语言为“+”连接符(字符串连接符)以及对象转换为字符串提供了特殊的支持,字符串对象可以使用“+”连接其他对象。String类的部分源码如下

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0
        ...
    }
    

    从上面可以看出
    1)String类被final关键字修饰,意味着String类不能被继承,并且它的成员方法都默认为final方法;字符串一旦创建就不能再修改。
    2)String类实现了Serializable、CharSequence、 Comparable接口。
    3)String实例的值是通过字符数组实现字符串存储的。


    1. “+”连接符

    1.1 “+”连接符的实现原理

    Java语言为“+”连接符以及对象转换为字符串提供了特殊的支持,字符串对象可以使用“+”连接其他对象。其中字符串连接是通过 StringBuilder(或 StringBuffer)类及其append 方法实现的,对象转换为字符串是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。有关字符连接和转换的更多信息,可以参阅 Gosling、Joy 和 Steele 合著的 《The Java Language Specification》

    我们可以通过反编译验证一下

    /**
     * 测试代码
     */
    public class Test {
        public static void main(String[] args) {
            int i = 10;
            String s = "abc";
            System.out.println(s + i);
        }
    }
    
    /**
     * 反编译后
     */
    public class Test {
        public static void main(String args[]) {    //删除了默认构造函数和字节码
            byte byte0 = 10;      
            String s = "abc";      
            System.out.println((new StringBuilder()).append(s).append(byte0).toString());
        }
    }
    

    由上可以看出,Java中使用"+"连接字符串对象时,会创建一个StringBuilder()对象,并调用append()方法将数据拼接,最后调用toString()方法返回拼接好的字符串。由于append()方法的各种重载形式会调用String.valueOf方法,所以我们可以认为:

    //以下两者是等价的
    s = i + ""
    s = String.valueOf(i);
     
    //以下两者也是等价的
    s = "abc" + i;
    s = new StringBuilder("abc").append(i).toString();
    

    1.2 “+”连接符的效率

    使用“+”连接符时,JVM会隐式创建StringBuilder对象,这种方式在大部分情况下并不会造成效率的损失,不过在进行大量循环拼接字符串时则需要注意。

    String s = "abc";
    for (int i=0; i<10000; i++) {
        s += "abc";
    }
    
    /**
     * 反编译后
     */
    String s = "abc";
    for(int i = 0; i < 1000; i++) {
         s = (new StringBuilder()).append(s).append("abc").toString();    
    }
    

    这样由于大量StringBuilder创建在堆内存中,肯定会造成效率的损失,所以在这种情况下建议在循环体外创建一个StringBuilder对象调用append()方法手动拼接(如上面例子如果使用手动拼接运行时间将缩小到1/200左右)。

    /**
     * 循环中使用StringBuilder代替“+”连接符
     */
    StringBuilder sb = new StringBuilder("abc");
    for (int i = 0; i < 1000; i++) {
        sb.append("abc");
    }
    sb.toString();
    

    与此之外还有一种特殊情况,也就是当"+"两端均为编译期确定的字符串常量时,编译器会进行相应的优化,直接将两个字符串常量拼接好,例如:

    System.out.println("Hello" + "World");
    
    /**
     * 反编译后
     */
    System.out.println("HelloWorld");
    
    /**
     * 编译期确定
     * 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。
     * 所以此时的"a" + s1和"a" + "b"效果是一样的。故结果为true。
     */
    String s0 = "ab"; 
    final String s1 = "b"; 
    String s2 = "a" + s1;  
    System.out.println((s0 == s2)); //result = true
    
    /**
     * 编译期无法确定
     * 这里面虽然将s1用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定
     * 因此s0和s2指向的不是同一个对象,故上面程序的结果为false。
     */
    String s0 = "ab"; 
    final String s1 = getS1(); 
    String s2 = "a" + s1; 
    System.out.println((s0 == s2)); //result = false 
     
    public String getS1() {  
        return "b";   
    }
    

    综上,“+”连接符对于直接相加的字符串常量效率很高,因为在编译期间便确定了它的值,也就是说形如"I"+“love”+“java”; 的字符串相加,在编译期间便被优化成了"Ilovejava"。对于间接相加(即包含字符串引用,且编译期无法确定值的),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。

    2. 字符串常量池

    在Java的内存分配中,总共3种常量池,分别是Class常量池运行时常量池字符串常量池

    字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性,常量池中一定不存在两个相同的字符串

    /**
     * 字符串常量池中的字符串只存在一份!
     * 运行结果为true
     */
    String s1 = "hello world!";
    String s2 = "hello world!";
    System.out.println(s1 == s2);
    

    2.1 内存区域

    在HotSpot VM中字符串常量池是通过一个StringTable类实现的,它是一个Hash表,默认值大小长度是1009;这个StringTable在每个HotSpot VM的实例中只有一份,被所有的类共享;字符串常量由一个一个字符组成,放在了StringTable上。要注意的是,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。

    在JDK6及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中的,StringTable的长度是固定的1009;在JDK7版本中,字符串常量池被移到了堆中,StringTable的长度可以通过**-XX:StringTableSize=66666**参数指定。至于JDK7为什么把常量池移动到堆上实现,原因可能是由于方法区的内存空间太小且不方便扩展,而堆的内存空间比较大且扩展方便。

    2.2 存放的内容

    在JDK6及之前版本中,String Pool里放的都是字符串常量;在JDK7.0中,由于String.intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用。

    /**
     * 运行结果为true false
     */
    String s1 = "AB";
    String s2 = "AB";
    String s3 = new String("AB");
    System.out.println(s1 == s2);
    System.out.println(s1 == s3);
    

    由于常量池中不存在两个相同的对象,所以s1和s2都是指向JVM字符串常量池中的"AB"对象。new关键字一定会产生一个对象,并且这个对象存储在堆中。所以String s3 = new String(“AB”);产生了两个对象:保存在栈中的s3和保存堆中的String对象。
    这里写图片描述

    当执行String s1 = "AB"时,JVM首先会去字符串常量池中检查是否存在"AB"对象,如果不存在,则在字符串常量池中创建"AB"对象,并将"AB"对象的地址返回给s1;如果存在,则不创建任何对象,直接将字符串常量池中"AB"对象的地址返回给s1。

    3. intern方法

    直接使用双引号声明出来的String对象会直接存储在字符串常量池中,如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法是一个native方法,intern方法会从字符串常量池中查询当前字符串是否存在,如果存在,就直接返回当前字符串;如果不存在就会将当前字符串放入常量池中,之后再返回。

    JDK1.7的改动:

    1. 将String常量池 从 Perm 区移动到了 Java Heap区
    2. String.intern() 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。
    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();
    

    3.1 intern的用法

    static final int MAX = 1000 * 10000;
    static final String[] arr = new String[MAX];
    
    public static void main(String[] args) throws Exception {
        Integer[] DB_DATA = new Integer[10];
        Random random = new Random(10 * 10000);
        for (int i = 0; i < DB_DATA.length; i++) {
            DB_DATA[i] = random.nextInt();
        }
        long t = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
             arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
        }
    
        System.out.println((System.currentTimeMillis() - t) + "ms");
        System.gc();
    }
    

    运行的参数是:-Xmx2g -Xms2g -Xmn1500M 上述代码是一个演示代码,其中有两条语句不一样,一条是未使用 intern,一条是使用 intern。结果如下图

    未使用intern,耗时826ms:
    这里写图片描述
    使用intern,耗时2160ms:
    这里写图片描述
    通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

    细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后,然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern 占用空间导致 jvm 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。

    4. String、StringBuilder和StringBuffer

    4.1 继承结构

    这里写图片描述

    4.2 主要区别

    1)String是不可变字符序列,StringBuilder和StringBuffer是可变字符序列。
    2)执行速度StringBuilder > StringBuffer > String。
    3)StringBuilder是非线程安全的,StringBuffer是线程安全的。

    5. 总结

    String类是我们使用频率最高的类之一,也是面试官经常考察的题目,下面是一个小测验。

    public static void main(String[] args) {
        String s1 = "AB";
        String s2 = new String("AB");
        String s3 = "A";
        String s4 = "B";
        String s5 = "A" + "B";
        String s6 = s3 + s4;
        System.out.println(s1 == s2);
        System.out.println(s1 == s5);
        System.out.println(s1 == s6);
        System.out.println(s1 == s6.intern());
        System.out.println(s2 == s2.intern());
    }
    

    运行结果:
    这里写图片描述

    解析:真正理解此题目需要清楚以下三点
    1)直接使用双引号声明出来的String对象会直接存储在常量池中;
    2)String对象的intern方法会得到字符串对象在常量池中对应的引用,如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
    3) 字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c命令获得class文件对应的JVM字节码指令就可以看出来。
    这里写图片描述

    参考文献
    https://docs.oracle.com/javase/8/docs/api/
    https://blog.csdn.net/sinat_19425927/article/details/38663461
    https://www.cnblogs.com/xiaoxi/p/6036701.html
    https://tech.meituan.com/in_depth_understanding_string_intern.html

    展开全文
  • Java 动态为类添加属性

    千次阅读 2019-10-11 09:28:37
    “衰兰送客咸阳道,天若有情天亦老” 序 有一个这样的功能:展示一个报表,表头是动态的,可以手动新增或删除,也就是说会修改表结构,...但是这里说说给实体动态添加属性。 实现代码 import com.google.common...

    “衰兰送客咸阳道,天若有情天亦老”

    有一个这样的功能:展示一个报表,表头是动态的,可以手动新增或删除,也就是说会修改表结构,新增或删除表字段。然而一般实体类都是对应表结构的,那么如何给实体增加属性呢?虽然我自己的解决方案是直接返回List<map>,然后让对应的列适配到map的key中。但是这里说说给实体类动态添加属性。

    实现代码

    
    import com.google.common.collect.Maps;
    import net.sf.cglib.beans.BeanGenerator;
    import net.sf.cglib.beans.BeanMap;
    import org.apache.commons.beanutils.PropertyUtilsBean;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     *@author 
     *@date 2019/10/9
     *@description
     */
    public class ReflectUtil {
    
        static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
    
        public static Object getTarget(Object dest, Map<String, Object> addProperties) {
            // get property map
            PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
            PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
            Map<String, Class> propertyMap = Maps.newHashMap();
            for (PropertyDescriptor d : descriptors) {
                if (!"class".equalsIgnoreCase(d.getName())) {
                    propertyMap.put(d.getName(), d.getPropertyType());
                }
            }
            // add extra properties
            for (Map.Entry<String, Object> entry : addProperties.entrySet()) {
                propertyMap.put(entry.getKey(), entry.getValue().getClass());
            }
    //        addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
            // new dynamic bean
            DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
            // add old value
            for (Map.Entry<String, Class> entry : propertyMap.entrySet()) {
                try {
                    // filter extra properties
                    if (!addProperties.containsKey(entry.getKey())) {
                        dynamicBean.setValue(entry.getKey(), propertyUtilsBean.getNestedProperty(dest, entry.getKey()));
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
            ;
            // add extra value
            for (Map.Entry<String, Object> entry : addProperties.entrySet()) {
                try {
                    dynamicBean.setValue(entry.getKey(), entry.getValue());
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
            ;
            Object target = dynamicBean.getTarget();
            return target;
        }
    
        public static class DynamicBean {
            /**
             * 目标对象
             */
            private Object target;
    
            /**
             * 属性集合
             */
            private BeanMap beanMap;
    
            public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
                this.target = generateBean(superclass, propertyMap);
                this.beanMap = BeanMap.create(this.target);
            }
    
    
            /**
             * bean 添加属性和值
             *
             * @param property
             * @param value
             */
            public void setValue(String property, Object value) {
                beanMap.put(property, value);
            }
    
            /**
             * 获取属性值
             *
             * @param property
             * @return
             */
            public Object getValue(String property) {
                return beanMap.get(property);
            }
    
            /**
             * 获取对象
             *
             * @return
             */
            public Object getTarget() {
                return this.target;
            }
    
    
            /**
             * 根据属性生成对象
             *
             * @param superclass
             * @param propertyMap
             * @return
             */
            private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
                BeanGenerator generator = new BeanGenerator();
                if (null != superclass) {
                    generator.setSuperclass(superclass);
                }
                BeanGenerator.addProperties(generator, propertyMap);
                return generator.create();
            }
        }
    
        public static void main(String[] args) {
            TestClass entity = new TestClass();
            entity.setAge(15);
            entity.setId(1);
            entity.setName("张三");
            //动态设置属性字段
            Map<String, Object> addProperties = new HashMap() {{
                put("sex", "男");
                put("telephone", "123456789");
            }};
            //获取实体中给属性的值
            Object target = getTarget(entity, addProperties);
            System.out.println(getFieldValueByName("id", target));
            System.out.println(getFieldValueByName("name", target));
            System.out.println(getFieldValueByName("age", target));
            System.out.println(getFieldValueByName("sex", target));
            System.out.println(getFieldValueByName("telephone", target));
        }
    
        //获取实体中属性的值
        private static Object getFieldValueByName(String fieldName, Object o) {
            try {
                //通过反射获取值
                String firstLetter = fieldName.substring(0, 1).toUpperCase();
                String getter = "get" + firstLetter + fieldName.substring(1);
                Method method = o.getClass().getMethod(getter, new Class[]{});
                Object value = method.invoke(o, new Object[]{});
                return value;
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return null;
            }
        }
    
        //测试类
        public static class TestClass{
            //ID
            private int id;
            //名字
            private String name;
            //年龄
            private int age;
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public int getAge() {
                return age;
            }
    
            public void setAge(int age) {
                this.age = age;
            }
        }
    
    }
    
    

    结果:
    在这里插入图片描述
    本文参考至:JAVA“动态”为类添加属性

    展开全文
  • Java 动态添加属性字段

    万次阅读 2018-10-22 18:35:11
    2.建一个实体对象 DynamicBean.java 。主要用来处理对象。 public class DynamicBean { private Object object = null; // 动态生成的 private BeanMap beanMap = null; // 存放属性名称以及属性的类型 ...

    说明:做项目中遇到一种场景,需要根据查询时间段, 获取时间段中中每个月份对应的金额(费用统计)。如截图中的两列

    因为列是动态的, 首先想到的就是后天拼接JSON格式字符串, 然后返回到前台, 组装表头及内容。 但是当前系统中easyUI版本为1.2,并不支持 data属性(官方从1.3.2开始支持)。所以只能返回list<T> 格式。

    网上一搜相关代码很多, 看客可以自己搜索一下。 我这里记录一下我当时使用场景及用法,已备以后使用。

     

    1.需要引用cglib jar包, 我用的版本是2.2

    2.建一个实体对象 DynamicBean.java 。主要用来处理对象。

    public class DynamicBean {
        private Object object = null; // 动态生成的类
    
        private BeanMap beanMap = null; // 存放属性名称以及属性的类型
    
        public DynamicBean() {
            super();
        }
    
        public DynamicBean(Map propertyMap) {
            this.object = generateBean(propertyMap);
            this.beanMap = BeanMap.create(this.object);
        }
    
        /**
         * @param propertyMap
         * @return
         */
        private Object generateBean(Map propertyMap) {
            BeanGenerator generator = new BeanGenerator();
            Set keySet = propertyMap.keySet();
            for (Iterator<String> i = keySet.iterator(); i.hasNext();) {
                String key = (String) i.next();
                generator.addProperty(key, (Class) propertyMap.get(key));
            }
            return generator.create();
        }
    
        /**
         * ��bean���Ը�ֵ
         * @param property ������
         * @param value ֵ
         */
        public void setValue(Object property, Object value) {
            beanMap.put(property, value);
        }
    
        /**
         * ͨ���������õ�����ֵ
         * @param property ������
         * @return ֵ
         */
        public Object getValue(String property) {
            return beanMap.get(property);
        }
    
        /**
         * 返回新生成的对象
         * @return
         */
        public Object getObject() {
            return this.object;
        }
    }

    3. 原来对象, 及需要拼接到对象中的属性字段集合处理方法。

    /**
    *参数说明:
    *  object : 查询结果数组中对象。
    *  moneyMap : 为对象对应所有月份数据集合
    *  解释:已经查询出一组账单对象集合List<Bill> , 而moneyMap为对象中的一个属性 
    *  Map<String,Bigdecimal>, 存放了月份及金额
    */
    private Object dynamicClass(Object object, Map<String, BigDecimal> moneyMap) throws Exception {
            // 字段 - 值 集合
            HashMap<String, Object> returnMap = new HashMap<String, Object>();
            // 字段 - 字段类型 集合
            HashMap<String, Object> typeMap = new HashMap<String, Object>();
            // 获取传入类
            Class<? extends Object> type = object.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            // 获取对象中已存在的数据
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class") && !propertyName.equals("monthMap")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(object, new Object[0]);
                    if (result != null) {
                        returnMap.put(propertyName, result);
                    } else {
                        String propertyType = descriptor.getPropertyType().toString();
                        if (propertyType.contains("java.math.BigDecimal")) {
                            returnMap.put(propertyName, new BigDecimal(0));
                        } else {
                            returnMap.put(propertyName, "");
                        }
                    }
                    typeMap.put(propertyName, descriptor.getPropertyType());
                }
            }
            // 获取月份数据, 变为字段属性
            Set<String> monthKeys = moneyMap.keySet();
            for (Iterator<String> it = monthKeys.iterator(); it.hasNext();) {
                String key = (String) it.next();
                // 字段类型
                typeMap.put(key, Class.forName("java.math.BigDecimal"));
                // 字段对应值
                returnMap.put(key, moneyMap.get(key));
            }
            // map转换成实体对象
            DynamicBean bean = new DynamicBean(typeMap);
            // 赋值
            Set<String> keys = typeMap.keySet();
            for (Iterator<String> it = keys.iterator(); it.hasNext();) {
                String key = (String) it.next();
                bean.setValue(key, returnMap.get(key));
            }
            Object obj = bean.getObject();
            return obj;
        }

     

    做笔记使用, 说不定以后还会用到。

    展开全文
  • java抽象

    万次阅读 多人点赞 2019-05-13 21:39:28
    下面小编给大家讲一个java的抽象,在面向对象的概念中,所有的对象都是通过来描绘的,但是反过来,并不是所有的都是用来描绘对象的,如果一个中没有包含足够的信息来描绘一个具体的对象,这样的就是抽象...

    开发工具与关键技术:MyEclipse 10、java语言、
    作者:邓李庆
    撰写时间: 2019年5月10日
    下面小编给大家讲一个java的抽象类,在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类;抽象来除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法、初始化块和构造方法和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
    抽象方法:只包含一个方法名,而没有方法体,使用abstract修饰
    格式: {修饰符} abstract 返回类型 方法名();
    修饰符(pubic protected private)三选一,见代码图1:
    在这里插入图片描述
    图1
    抽象类必须使用abstract修饰符来修饰。抽象方法也必须使用abstract修饰符来修饰,不能有方法体。
    抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例;
    抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。Abstract static不能同时修饰一个方法
    接口(interface),在java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
    在java中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象;修饰符public、abstract;接口的特性:
    1)接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    2)接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
    3)接口中的方法都是公有的。
    4)编译时自动为接口里定义的方法添加public abstract修饰符
    5)java接口里的成员变量只能是public static final共同修饰的,并且必须赋初值,可以不写public static final,编译的时候会自动添加。
    见代码图2:
    在这里插入图片描述
    图2
    当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。类使用implements关键字实现接口。在类声明中,implements关键字放在class声明后面。
    在这里插入图片描述
    图3
    一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。接口允许多继承;抽象类和接口差异:

    抽象类接口
    默认的方法实现它可以有默认的方法实现接口完全是抽象的。它根本不存在方法的实现
    实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的抽象方法的实现类使用关键字impements来实现接口。它需要提供接口中所有声明的方法的实现
    构造器抽象类可以有构造器接口不能有构造器
    与正常java类的区别除了不能实例化抽象类之外,它和普通java类没有任何区别接口是完全不同的类型
    访问修饰符抽象方法可以有pubic、protected和default这些修饰符接口方法默认修饰符是public。你不可以使用其它修饰符
    main方法抽象方法可以有main方法并且我们可以运行它接口没有main方法,因此我们不能运行它
    多继承抽象类可以继承一个类和实现多个接口接口只可以继承一个或多个其它接口
    添加新方法如果你往抽象类中添加新的方法,你可以给它提供默认的实现,因此你不需要改变你现在的代码如果你往接口中添加方法,那么你必须改变实现接口的类
    展开全文
  • 我竟然不再抗拒 Java加载机制了

    万次阅读 多人点赞 2019-07-10 12:47:50
    很长一段时间里,我对 Java加载机制都非常的抗拒,因为我觉得太难理解了。但为了成为一名优秀的 Java 工程师,我决定硬着头皮研究一下。 01、字节码 在聊 Java 加载机制之前,需要先了解一下 Java 字节码,...
  • 封装一个的实例对象的数据,你需要声明其属性变量为 private ,然后提供访问器方法。 访问器方法的命名严格遵守JavaBean模式。在这种模式下的一个示例:属性foo有一个getter方法为getFoo(),有一个setter方法为...
  • Java添加背景音乐

    万次阅读 2014-07-10 11:30:52
    Java添加背景音乐
  • java动态给实体类添加属性

    千次阅读 2020-04-10 11:01:19
    java动态给实体类添加属性 需要的jar包有两个:cglib-3.1.jar 和 google-collections-1.0.jar 如果是maven项目,就不需要cglib-3.1.jar,直接用spring自带的 org.springframework.cglib即可。 1、创建一个实体:...
  • Java 分类导航

    万次阅读 2018-12-27 20:32:56
    准备 MyEclipse 快捷键大全 JAVA - JDK 1.8 API 帮助文档-中文版 JAVA 各阶段学习 - 推荐书籍 Java 快速入门 Java快速入门-01-基础篇 Java快速入门-02-基础篇 Java快速入门-03-小知识汇总篇(全) Java快速入门...
  • java如何向数组里添加元素

    万次阅读 多人点赞 2018-09-14 23:54:52
    java篇 哇,菜鸟第一次写这个东西,当加深印象,大佬们请略过,欢迎有错指出。 向数组里添加一个元素怎么添加,这儿总结有三种方法: 1、一般数组是不能添加元素的,因为他们在初始化时就已定好长度了,不能...
  • /** *describe: * *@author xxx *@date ${YEAR}/${MONTH}/${DAY} */
  • Java Scanner的常用方法及用法(很详细)

    万次阅读 多人点赞 2018-08-24 21:16:23
    Java 5添加java.util.Scanner,这是一个用于扫描输入文本的新的实用程序。它是以前的StringTokenizer和Matcher之间的某种结合。由于任何数据都必须通过同一模式的捕获组检索或通过使用一个索引来检索文本的...
  • eclipse为java类添加版权注释

    千次阅读 2013-05-18 21:35:22
    位置:Window/reference/java/codestyle/code/new java files ${filecomment} ${package_declaration} /** * 项目名称:${project_name} * 名称:${type_name} * 描述: * 创建人:吕德奎 * 作者...
  • JAVA Future详解

    万次阅读 多人点赞 2018-06-06 17:24:24
    在并发编程中,我们经常用到非阻塞的模型,在之前的多线程的三种实现中,不管是继承thread还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。
  • Java final详解

    千次阅读 2018-03-11 13:54:52
    虽然在Java编程中并不经常使用final和final方法,但它们有着与众不同的特点,即final不能被继承,不能被覆盖,以及final在执行速度方面比一般快。2.为何不能继承final?有时需要对继承加以限制。例如某些...
  • Java动态生成以及动态添加属性

    万次阅读 2017-11-18 16:14:20
    有个技术实现需求:动态生成,其中中的属性来自参数对象中的全部属性以及来自参数对象properties文件。 那么技术实现支持:使用CGLib代理。 具体的实现步骤: 1.配置Maven文件: xmlns:xsi=...
  • Java Object方法

    万次阅读 2018-10-22 11:28:56
     (1)Object位于java.lang包中,java.lang包包含着Java最基础和核心的,在编译时会自动导入;  (2)Object是所有Java类的祖先。每个都使用 Object 作为超类。所有对象(包括数组)都实现这个的方法。可以...
  • java内部面试题: java内部分类: 成员内部,局部内部,静态内部,匿名内部 静态内部不能访问外部的属性方法,所以android开发过程中可以优化handler 其他内部可以访问外部,是因为持有外部...
  • 面试官,不要再问我“Java虚拟机加载机制”了

    万次阅读 多人点赞 2019-10-27 16:28:39
    关于Java虚拟机加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机加载机制的流程。其实这两题本质上都是考察面试者对Java虚拟机加载机制的了解。 面试题试水 现在有这样一道判断程序输出结果...
  • java抽象 与 接口

    千次阅读 2015-01-12 16:13:39
    Java接口和Java抽象的认识 很难理解为什么要有接口这个概念,虽说是可以实现所谓的多继承,可一个只有方法名,没有方法体的东西,我实现它又有什么用呢?我从它那什么也得不到,除了一些方法名,我直接在具体...
  • 本来是在看多线程方面的知识,做demo时在实现监听器添加的时候想遵循《java核心技术》中的推荐写法: 监听器最好是单独形成一个(可以是外部或者是内部),不要使用直接在本中扩展actionListener接口,...
  • Java中的线程池有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个都继承自ExecutorService。利用这两个,可以创建各种不同的Java线程池,为了方便我们创建线程池,Java API提供了...
  • Java的概念

    万次阅读 多人点赞 2019-04-25 19:57:41
    Java的概念 是一个模板,它描述一对象的行为和状态。 对象:对象是的一个实例,有状态和行为。例如,一条狗是一个对象,它的状 态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等 Java中的 定义一...
  • Idea创建java类时,自动添加注释信息

    千次阅读 2018-08-14 11:31:13
    设置方法:File -&...如图所示,在模板头部添加: /** * Created with IntelliJ IDEA. * User: ${USER} * Date: ${DATE} * Time: ${TIME} * Description: No Description */ 然后点击apply或ok  ...
  • Java动态加载

    万次阅读 2011-11-29 10:41:26
    Java动态加载的意义和目的: Java动态加载主要是为了不改变主程序代码,通过修改配置文件就可以操作不同的对象执行不同的功能。主要有利于系统的扩展,例如当我要改变一个功能,只需要做一个,然后编写相应的...
  • Java中swing使用ImageIcon类添加图片

    万次阅读 2017-05-25 19:07:53
    代码如下 package ... ...其中 images/logo.jpg 是图片的路径,记得images是项目(Project)目录下的文件夹,跟src同级的。...java ImageIcon路径问题 本文链接: https://liuyanzhao.com/4081.html
  • 可以添加,但是在声明对象时只有声明本身才能调用到。 例如:接口A,有ab两个方法,S实现A,并增加了方法c。 声明: A s=new S();则s只能调用ab,不能调c。 声明S s=new S();s可以调用abc方法。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,444,272
精华内容 577,708
关键字:

java怎么添加类

java 订阅