第一个_第一个程序hello world - CSDN
  • 第一个

    2016-10-11 17:51:40
    四、Java的数据类型 1、Java的基本数据类型都有哪些各占几字节 ...boolean 1(boolean类型比较特别可能只占一个bit,多boolean可能共同占用一个字节) 2、String是基本数据类型吗?可以被继承吗? String是引

    四、Java的数据类型

    1、Java的基本数据类型都有哪些各占几个字节

    Java有8种基本数据类型

    byte 1

    char 2

    sort 2

    int 4

    float 4

    double 8

    long 8

    boolean 1(boolean类型比较特别可能只占一个bit,多个boolean可能共同占用一个字节)

    2、String是基本数据类型吗?可以被继承吗?

    String是引用类型,底层用char数组实现的。因为String是final类,在java中被final修饰的类不能被继承,因此String当然不可以被继承。

     

    五、Java的IO

    1、Java中有几种类型的流

    字节流和字符流。字节流继承于InputStream和OutputStream,字符流继承于InputStreamReader 和OutputStreamWriter。

    2、字节流如何转为字符流

    字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。

    字节输出流转字符输出流通过OutputStreamWriter实现,该类的构造函数可以传入OutputStream对象。

    3、如何将一个java对象序列化到文件里

    java中能够被序列化的类必须先实现Serializable接口,该接口没有任何抽象方法只是起到一个标记作用。

    六、Java的集合

    1、HashMap排序题,上机题。(本人主要靠这道题入职的第一家公司)

    已知一个HashMap<Integer,User>集合, User有name(String)和age(int)属性。请写一个方法实现对HashMap的排序功能,该方法接收HashMap<Integer,User>为形参,返回类型为HashMap<Integer,User>,要求对HashMap中的User的age倒序进行排序。排序时key=value键值对不得拆散。

    :要做出这道题必须对集合的体系结构非常的熟悉。HashMap本身就是不可排序的,但是该道题偏偏让给HashMap排序,那我们就得想在API中有没有这样的Map结构是有序的,LinkedHashMap,对的,就是他,他是Map结构,也是链表结构,有序的,更可喜的是他是HashMap的子类,我们返回LinkedHashMap<Integer,User>即可,还符合面向接口(父类编程的思想)。

    但凡是对集合的操作,我们应该保持一个原则就是能用JDK中的API就有JDK中的API,比如排序算法我们不应该去用冒泡或者选择,而是首先想到用Collections集合工具类。

     

    2、集合的安全性问题

    请问ArrayList、HashSet、HashMap是线程安全的吗?如果不是我想要线程安全的集合怎么办?

    我们都看过上面那些集合的源码(如果没有那就看看吧),每个方法都没有加锁,显然都是线程不安全的。话又说过来如果他们安全了也就没第二问了。

    在集合中Vector和HashTable倒是线程安全的。你打开源码会发现其实就是把各自核心方法添加上了synchronized关键字。

    Collections工具类提供了相关的API,可以让上面那3个不安全的集合变为安全的。

    上面几个函数都有对应的返回值类型,传入什么类型返回什么类型。打开源码其实实现原理非常简单,就是将集合的核心方法添加上了synchronized关键字。

    3、ArrayList内部用什么实现的?(2015-11-24)

    (回答这样的问题,不要只回答个皮毛,可以再介绍一下ArrayList内部是如何实现数组的增加和删除的,因为数组在创建的时候长度是固定的,那么就有个问题我们往ArrayList中不断的添加对象,它是如何管理这些数组呢?)

    ArrayList内部是用Object[]实现的。接下来我们分别分析ArrayList的构造、add、remove、clear方法的实现原理。

    一、构造函数

    1)空参构造

    /**

         * Constructs a new {@code ArrayList} instance with zero initial capacity.

         */

        public ArrayList() {

            array = EmptyArray.OBJECT;

    }

    array是一个Object[]类型。当我们new一个空参构造时系统调用了EmptyArray.OBJECT属性,EmptyArray仅仅是一个系统的类库,该类源码如下:

    public final class EmptyArray {

        private EmptyArray() {}

     

        public static final boolean[] BOOLEAN = new boolean[0];

        public static final byte[] BYTE = new byte[0];

        public static final char[] CHAR = new char[0];

        public static final double[] DOUBLE = new double[0];

        public static final int[] INT = new int[0];

     

        public static final Class<?>[] CLASS = new Class[0];

        public static final Object[] OBJECT = new Object[0];

        public static final String[] STRING = new String[0];

        public static final Throwable[] THROWABLE = new Throwable[0];

        public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];

    }

    也就是说当我们new 一个空参ArrayList的时候,系统内部使用了一个new Object[0]数组。

    2)带参构造1

     /**

         * Constructs a new instance of {@code ArrayList} with the specified

         * initial capacity.

         *

         * @param capacity

         *            the initial capacity of this {@code ArrayList}.

         */

        public ArrayList(int capacity) {

            if (capacity < 0) {

                throw new IllegalArgumentException("capacity < 0: " + capacity);

            }

            array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);

    }

    该构造函数传入一个int值,该值作为数组的长度值。如果该值小于0,则抛出一个运行时异常。如果等于0,则使用一个空数组,如果大于0,则创建一个长度为该值的新数组。

    3)带参构造2

    /**

         * Constructs a new instance of {@code ArrayList} containing the elements of

         * the specified collection.

         *

         * @param collection

         *            the collection of elements to add.

         */

        public ArrayList(Collection<? extends E> collection) {

            if (collection == null) {

                throw new NullPointerException("collection == null");

            }

     

            Object[] a = collection.toArray();

            if (a.getClass() != Object[].class) {

                Object[] newArray = new Object[a.length];

                System.arraycopy(a, 0, newArray, 0, a.length);

                a = newArray;

            }

            array = a;

            size = a.length;

        }

    如果调用构造函数的时候传入了一个Collection的子类,那么先判断该集合是否为null,为null则抛出空指针异常。如果不是则将该集合转换为数组a,然后将该数组赋值为成员变量array,将该数组的长度作为成员变量size。这里面它先判断a.getClass是否等于Object[].class,其实一般都是相等的,我也暂时没想明白为什么多加了这个判断,toArray方法是Collection接口定义的,因此其所有的子类都有这样的方法,list集合的toArray和Set集合的toArray返回的都是Object[]数组。

    这里讲些题外话,其实在看Java源码的时候,作者的很多意图都很费人心思,我能知道他的目标是啥,但是不知道他为何这样写。比如对于ArrayList, array是他的成员变量,但是每次在方法中使用该成员变量的时候作者都会重新在方法中开辟一个局部变量,然后给局部变量赋值为array,然后再使用,有人可能说这是为了防止并发修改array,毕竟array是成员变量,大家都可以使用因此需要将array变为局部变量,然后再使用,这样的说法并不是都成立的,也许有时候就是老外们写代码的一个习惯而已。

    二、add方法

    add方法有两个重载,这里只研究最简单的那个。

          /**

         * Adds the specified object at the end of this {@code ArrayList}.

         *

         * @param object

         *            the object to add.

         * @return always true

         */

        @Override public boolean add(E object) {

            Object[] a = array;

            int s = size;

            if (s == a.length) {

                Object[] newArray = new Object[s +

                        (s < (MIN_CAPACITY_INCREMENT / 2) ?

                         MIN_CAPACITY_INCREMENT : s >> 1)];

                System.arraycopy(a, 0, newArray, 0, s);

                array = a = newArray;

            }

            a[s] = object;

            size = s + 1;

            modCount++;

            return true;

        }

    1、首先将成员变量array赋值给局部变量a,将成员变量size赋值给局部变量s。

    2、判断集合的长度s是否等于数组的长度(如果集合的长度已经等于数组的长度了,说明数组已经满了,该重新分配新数组了),重新分配数组的时候需要计算新分配内存的空间大小,如果当前的长度小于MIN_CAPACITY_INCREMENT/2(这个常量值是12,除以2就是6,也就是如果当前集合长度小于6)则分配12个长度,如果集合长度大于6则分配当前长度s的一半长度。这里面用到了三元运算符和位运算,s >> 1,意思就是将s往右移1位,相当于s=s/2,只不过位运算是效率最高的运算。

    3、将新添加的object对象作为数组的a[s]个元素。

    4、修改集合长度size为s+1

    5、modCotun++,该变量是父类中声明的,用于记录集合修改的次数,记录集合修改的次数是为了防止在用迭代器迭代集合时避免并发修改异常,或者说用于判断是否出现并发修改异常的。

    6、return true,这个返回值意义不大,因为一直返回true,除非报了一个运行时异常。

    三、remove方法

    remove方法有两个重载,我们只研究remove(int index)方法。

          /**

         * Removes the object at the specified location from this list.

         *

         * @param index

         *            the index of the object to remove.

         * @return the removed object.

         * @throws IndexOutOfBoundsException

         *             when {@code location < 0 || location >= size()}

         */

        @Override public E remove(int index) {

            Object[] a = array;

            int s = size;

            if (index >= s) {

                throwIndexOutOfBoundsException(index, s);

            }

            @SuppressWarnings("unchecked")

            E result = (E) a[index];

            System.arraycopy(a, index + 1, a, index, --s - index);

            a[s] = null;  // Prevent memory leak

            size = s;

            modCount++;

            return result;

        }

    1、先将成员变量array和size赋值给局部变量a和s。

    2、判断形参index是否大于等于集合的长度,如果成了则抛出运行时异常

    3、获取数组中脚标为index的对象result,该对象作为方法的返回值

    4、调用System的arraycopy函数,拷贝原理如下图所示。

     

    5、接下来就是很重要的一个工作,因为删除了一个元素,而且集合整体向前移动了一位,因此需要将集合最后一个元素设置为null,否则就可能内存泄露。

    6、重新给成员变量array和size赋值

    7、记录修改次数

    8、返回删除的元素(让用户再看最后一眼)

    四、clear方法

           /**

         * Removes all elements from this {@code ArrayList}, leaving it empty.

         *

         * @see #isEmpty

         * @see #size

         */

        @Override public void clear() {

            if (size != 0) {

                Arrays.fill(array, 0, size, null);

                size = 0;

                modCount++;

            }

        }

    如果集合长度不等于0,则将所有数组的值都设置为null,然后将成员变量size设置为0即可,最后让修改记录加1。

    4、并发集合和普通集合如何区别?(2015-11-24)

    并发集合常见的有ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。并发集合位于java.util.concurrent包下,是jdk1.5之后才有的,主要作者是Doug Lea(http://baike.baidu.com/view/3141057.htm)完成的。

    java中有普通集合、同步(线程安全)的集合、并发集合。普通集合通常性能最高,但是不保证多线程的安全性和并发的可靠性。线程安全集合仅仅是给集合添加了synchronized同步锁,严重牺牲了性能,而且对并发的效率就更低了,并发集合则通过复杂的策略不仅保证了多线程的安全又提高的并发时的效率。

    参考阅读:

     ConcurrentHashMap是线程安全的HashMap的实现,默认构造同样有initialCapacity和loadFactor属性,不过还多了一个concurrencyLevel属性,三属性默认值分别为16、0.75及16。其内部使用锁分段技术,维持这锁Segment的数组,在Segment数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。

    put操作:并没有在此方法上加上synchronized,首先对key.hashcode进行hash操作,得到key的hash值。hash操作的算法和map也不同,根据此hash值计算并获取其对应的数组中的Segment对象(继承自ReentrantLock),接着调用此Segment对象的put方法来完成当前操作。

    ConcurrentHashMap基于concurrencyLevel划分出了多个Segment来对key-value进行存储,从而避免每次put操作都得锁住整个数组。在默认的情况下,最佳情况下可允许16个线程并发无阻塞的操作集合对象,尽可能地减少并发时的阻塞现象。

    get(key)

        首先对key.hashCode进行hash操作,基于其值找到对应的Segment对象,调用其get方法完成当前操作。而Segment的get操作首先通过hash值和对象数组大小减1的值进行按位与操作来获取数组上对应位置的HashEntry。在这个步骤中,可能会因为对象数组大小的改变,以及数组上对应位置的HashEntry产生不一致性,那么ConcurrentHashMap是如何保证的?

        对象数组大小的改变只有在put操作时有可能发生,由于HashEntry对象数组对应的变量是volatile类型的,因此可以保证如HashEntry对象数组大小发生改变,读操作可看到最新的对象数组大小。

        在获取到了HashEntry对象后,怎么能保证它及其next属性构成的链表上的对象不会改变呢?这点ConcurrentHashMap采用了一个简单的方式,即HashEntry对象中的hash、key、next属性都是final的,这也就意味着没办法插入一个HashEntry对象到基于next属性构成的链表中间或末尾。这样就可以保证当获取到HashEntry对象后,其基于next属性构建的链表是不会发生变化的。

        ConcurrentHashMap默认情况下采用将数据分为16个段进行存储,并且16个段分别持有各自不同的锁Segment,锁仅用于put和remove等改变集合对象的操作,基于volatile及HashEntry链表的不变性实现了读取的不加锁。这些方式使得ConcurrentHashMap能够保持极好的并发支持,尤其是对于读远比插入和删除频繁的Map而言,而它采用的这些方法也可谓是对于Java内存模型、并发机制深刻掌握的体现。

    推荐博客地址:http://m.oschina.net/blog/269037

     

    七、Java的多线程

    1、多线程的两种创建方式

    java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接实现Runnable接口来重写run()方法实现线程。

    2、java中wait和sleep方法的不同?

    最大的不同是在等待时wait会释放锁,而sleep一直持有锁。wait通常被用于线程间交互,sleep通常被用于暂停执行。

    3、synchronized和volatile关键字的作用

    一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

    1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

    2)禁止进行指令重排序。

    volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;

    synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

    1.volatile仅能使用在变量级别;

    synchronized则可以使用在变量、方法、和类级别的

    2.volatile仅能实现变量的修改可见性,并不能保证原子性;

    synchronized则可以保证变量的修改可见性和原子性

    3.volatile不会造成线程的阻塞;

    synchronized可能会造成线程的阻塞。

    4.volatile标记的变量不会被编译器优化;

    synchronized标记的变量可以被编译器优化

    4、分析线程并发访问代码解释原因

    上面的代码执行完后输出的结果确定为1000吗?

    答案是不一定,或者不等于1000。这是为什么吗?

    java 的内存模型中每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。

    也就是说上面主函数中开启了1000个子线程,每个线程都有一个变量副本,每个线程修改变量只是临时修改了自己的副本,当线程结束时再将修改的值写入在主内存中,这样就出现了线程安全问题。因此结果就不可能等于1000了,一般都会小于1000。

    上面的解释用一张图表示如下:

    图片来自网络,非本人所绘

    5、什么是线程池,如何使用?

    线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。

    JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。

    然后调用他们的execute方法即可。

    6、请叙述一下您对线程池的理解?(2015-11-25)

    (如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)

    合理利用线程池能够带来三个好处。

    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

    第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    7、线程池的启动策略?(2015-11-25)

    官方对线程池的执行过程描述如下:

    1.  /*

    2.          * Proceed in 3 steps:

    3.          *

    4.          * 1. If fewer than corePoolSize threads are running, try to

    5.          * start a new thread with the given command as its first

    6.          * task.  The call to addWorker atomically checks runState and

    7.          * workerCount, and so prevents false alarms that would add

    8.          * threads when it shouldn't, by returning false.

    9.          *

    10.          * 2. If a task can be successfully queued, then we still need

    11.          * to double-check whether we should have added a thread

    12.          * (because existing ones died since last checking) or that

    13.          * the pool shut down since entry into this method. So we

    14.          * recheck state and if necessary roll back the enqueuing if

    15.          * stopped, or start a new thread if there are none.

    16.          *

    17.          * 3. If we cannot queue task, then we try to add a new

    18.          * thread.  If it fails, we know we are shut down or saturated

    19.          * and so reject the task.

    20.          */

    1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

    2、当调用execute() 方法添加一个任务时,线程池会做如下判断:

         a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

    b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。

    c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;

    d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。

    3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

    4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

    8、如何控制某个方法允许并发访问线程的个数?(2015-11-30)

    21. package com.yange;

    22. 

    23. import java.util.concurrent.Semaphore;

    24. /**

    25.  *

    26.  * @author wzy 2015-11-30

    27.  *

    28.  */

    29. public class SemaphoreTest {

    30. /*

    31. * permits the initial number of permits available. This value may be negative,

    32. in which case releases must occur before any acquires will be granted.

    33. fair true if this semaphore will guarantee first-in first-out granting of

    34. permits under contention, else false

    35. */

    36. static Semaphore semaphore = new Semaphore(5,true);

    37. public static void main(String[] args) {

    38. for(int i=0;i<100;i++){

    39. new Thread(new Runnable() {

    40. 

    41. @Override

    42. public void run() {

    43. test();

    44. }

    45. }).start();

    46. }

    47. 

    48. }

    49. 

    50. public static void test(){

    51. try {

    52.         //申请一个请求

    53. semaphore.acquire();

    54. } catch (InterruptedException e1) {

    55. e1.printStackTrace();

    56. }

    57. System.out.println(Thread.currentThread().getName()+"进来了");

    58. try {

    59. Thread.sleep(1000);

    60. } catch (InterruptedException e) {

    61. e.printStackTrace();

    62. }

    63. System.out.println(Thread.currentThread().getName()+"走了");

    64.    //释放一个请求

    65. semaphore.release();

    66. }

    67. }

    68. 

    可以使用Semaphore控制,第16行的构造函数创建了一个Semaphore对象,并且初始化了5个信号。这样的效果是控件test方法最多只能有5个线程并发访问,对于5个线程时就排队等待,走一个来一下。第33行,请求一个信号(消费一个信号),如果信号被用完了则等待,第45行释放一个信号,释放的信号新的线程就可以使用了。

     

    1. JavaSE高级(★★

    一、Java中的反射

    1、说说你对Java中反射的理解

    Java中的反射首先是能够获取到Java中要反射类的字节码,获取字节码有三种方法,1.Class.forName(className) 2.类名.class 3.this.getClass()。然后将字节码中的方法,变量,构造函数等映射成相应的Method、Filed、Constructor等类,这些类提供了丰富的方法可以被我们所使用。

    二、Java中的动态代理

    1、写一个ArrayList的动态代理类(笔试题)

    2、动静态代理的区别,什么场景使用?(2015-11-25)

    静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

        静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

        动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。

        还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。

    AOP编程就是基于动态代理实现的,比如著名的Spring框架、Hibernate框架等等都是动态代理的使用例子。

    三、Java中的设计模式&回收机制

    1、你所知道的设计模式有哪些

    Java中一般认为有23种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

    总体来说设计模式分为三大类:

    创建型模式,共五种:工厂方法模式抽象工厂模式单例模式建造者模式、原型模式。

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    2、单例设计模式

    最好理解的一种设计模式,分为懒汉式和饿汉式。

     饿汉式:

     懒汉式:

    3、工厂设计模式

    工厂模式分为工厂方法模式和抽象工厂模式。

     工厂方法模式

    工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

    多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

    静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

     普通工厂模式

     多个工厂方法模式

    该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

     静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

     抽象工厂模式

    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

    4、建造者模式(Builder)

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。

    5、适配器设计模式

     适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

     类的适配器模式

     对象的适配器模式

    基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。

     接口的适配器模式

    接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

    6、装饰模式(Decorator)

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

    7、策略模式(strategy)

    策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

    8、观察者模式(Observer)

    观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

    9、JVM垃圾回收机制和常见算法

    理论上来讲Sun公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同。

    GC(Garbage Collector)在回收对象前首先必须发现那些无用的对象,如何去发现定位这些无用的对象?常用的搜索算法如下:

    1)引用计数器算法(废弃)

    引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是“垃圾”了。

    引用计数器实现简单,效率高;但是不能解决循环引用问问题(A对象引用B对象,B对象又引用A对象,但是A,B对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。

    2)根搜索算法(使用)

    根搜索算法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain),当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的。

    GC Roots对象包括:

    a) 虚拟机栈(栈帧中的本地变量表)中的引用的对象。

    b) 方法区域中的类静态属性引用的对象。

    c) 方法区域中常量引用的对象。

    d) 本地方法栈中JNI(Native方法)的引用的对象。

    通过上面的算法搜索到无用对象之后,就是回收过程,回收算法如下:

    1)标记—清除算法(Mark-Sweep)(DVM使用的算法

    标记—清除算法包括两个阶段:“标记”和“清除”。在标记阶段,确定所有要回收的对象,并做标记。清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。标记—清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。

    2)复制算法(Copying)

    复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的JVM用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是1:1(大概是8:1)。

    3)标记—整理算法(Mark-Compact)

    标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。

    4)分代收集(Generational Collection)

    分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。垃圾算法的实现涉及大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同。

    10、谈谈JVM的内存结构和内存分配

    a) Java内存模型

    Java虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java栈和Java堆。

        1、方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会在运行时改变。

    常数池,源代码中的命名常量、String常量和static变量保存在方法区。

        2、Java Stack是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。

            最典型的Stack应用是方法的调用,Java虚拟机每调用一次方法就创建一个方法帧(frame),退出该方法则对应的  方法帧被弹出(pop)。栈中存储的数据也是运行时确定的。

        3、Java堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。

           堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java对象的内存总是在heap中分配。

    我们每天都在写代码,每天都在使用JVM的内存。

    b) java内存分配

         1、基础数据类型直接在栈空间分配;

         2、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;

         3、引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;

         4、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;

         5、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收;

         6、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放;

         7、字符串常量在 DATA 区域分配 ,this 在堆空间分配;

         8、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!

    11、Java中引用类型都有哪些?(重要)

    Java中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

     强引用(StrongReference)

    这个就不多说,我们写代码天天在用的就是强引用。如果一个对象被被人拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

    Java的对象是位于heap中的,heap中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下代码:

    第一行在heap堆中创建内容为“abc”的对象,并建立abc到该对象的强引用,该对象是强可及的。

    第二行和第三行分别建立对heap中对象的软引用和弱引用,此时heap中的abc对象已经有3个引用,显然此时abc对象仍是强可及的。

    第四行之后heap中对象不再是强可及的,变成软可及的。

    第五行执行之后变成弱可及的。

     软引用(SoftReference)

    如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

    软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。当gc决定要收集软引用时执行以下过程,以上面的softRef为例:

        1 首先将softRef的referent(abc)设置为null,不再引用heap中的new String("abc")对象。

        2 将heap中的new String("abc")对象设置为可结束的(finalizable)。

        3 当heap中的new String("abc")对象的finalize()方法被运行而且该对象占用的内存被释放, softRef被添加到它的ReferenceQueue(如果有的话)中。

       注意:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有。

       Soft Reference 指到的对象,即使没有任何 Direct Reference,也不会被清除。一直要到 JVM 内存不足且没有Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。

     弱引用(WeakReference)

    如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被gc扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

     虚引用(PhantomReference)

    "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。

    虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

    建立虚引用之后通过get方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get方法返回结果为null。先看一下和gc交互的过程再说一下他的作用。

      1 不把referent设置为null, 直接把heap中的new String("abc")对象设置为可结束的(finalizable)。

      2 与软引用和弱引用不同, 先把PhantomRefrence对象添加到它的ReferenceQueue中.然后在释放虚可及的对象。

     

    2. Android基础(★★★

    一、Android基本常识

    1、10个简单的linux命令

    mkdir 创建文件夹

    rmdir 删除文件夹

    rm 删除文件

    mv 移动文件

    cp 拷贝文件

    cat 查看文件

    tail 查看文件尾部

    more 分页查看文件

    cd 切换当前目录

    ls 列出文件清单

    reboot 重启

    date 显示日期

    cal  显示日历

    ps 查看系统进程相当于windows的任务管理器

    ifconfig 配置网络

    2、书写出android工程的目录结构

    src 源文件

    gen 生成的文件 R文件就在此

    android. jar 依赖的android sdk

    assets 资源文件

        bin 生成的字节码apk在此

    libs 依赖jar和so

    res 资源文件

    drawable

    drawable-hdpi

    layout

    menu

    values

    AndroidManifest.xml

    project.properties

    3、什么是ANR 如何避免它?

    Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR给用户。

    不同的组件发生ANR的时间不一样,主线程(Activity、Service)是5秒,BroadCastReceiver 是10秒。

    解决方案:

    将所有耗时操作,比如访问网络,Socket通信,查询大量SQL语句,复杂逻辑计算等都放在子线程中去,然后通过handler.sendMessage、runonUITread、AsyncTask等方式更新UI。无论如何都要确保用户界面操作的流畅度。如果耗时操作需要让用户等待,那么可以在界面上显示进度条。

    4、谈谈Android的优点和不足之处

    优点:

    1、开放性,开源,免费,可定制

    2、挣脱运营商束缚

    3、丰富的硬件选择

    4、不受任何限制的开发商

    5、无缝结合的Google应用

    缺点:

    1、安全问题、隐私问题

    2、同质化严重

    3、运营商对Android手机仍然有影响

    4、山寨化严重

    5、过分依赖开发商,缺乏标准配置

    5、一条最长的短信息约占多少byte? 

    在国内的三大运营商通常情况下中文70(包括标点),英文160个。对于国外的其他运行商具体多长需要看运营商类型了。

    android内部是通过如下代码进行判断具体一个短信多少byte的。

    ArrayList<String> android.telephony.SmsManager.divideMessage(String text)

    6、sim卡的EF文件有何作用?(不用看,废弃)

    基本文件EF(Elementary File)是SIM卡文件系统的一部分。

     

    文件

    文件标识符

    文件缩写

    中文名称

    文件作用

    MF

    3F00

    根目录

    备注:所有非ETSI GSM协议中规定的应用文件由各厂家自行定义在根目录下(如:PIN1,PIN2…)

    EFICCID

    2FE2

    ICCID

    SIM卡唯一的识别号

    包含运营商、卡商、发卡时间、省市代码等信息

    DFGSM

    7F20

    GSM目录

    备注:根据ETSIGSM09.91的规定Phase2(或以上)的SIM卡中应该有7F21并指向7F20,用以兼容Phase1的手机

    EFLP语言选择

    6F05

    LP

    语言选择文件

    包含一种或多种语言编码

    EFIMSI

    6F07

    IMSI

    国际移动用户识别符

    包含SIM卡所对应的号段,比如46000代表135-139号段、46002代表1340-1348

    EFKC语音加密密钥

    6F20

    Kc

    计算密钥

    用于SIM卡的加密、解密

    EFPLMNsel网络选择表

    6F30

    PLMNsel

    公共陆地网选择

    决定SIM卡选择哪种网络,在这里应该选择中国移动的网络

    EFHPLMN归属地网络选择表

    6F31

    HPLMN

    两次搜索PLMN的时间间隔

    两次搜索中国移动的网络的时间间隔

    EFACMmax最大计费额

    6F37

    ACMmax

    包含累积呼叫表的最大值

    全部的ACM数据存在SIM卡中,此处取最大值

    EFSST SIM卡服务表

    6F38

    SST

    SIM卡服务列表

    指出SIM卡可以提供服务的种类,哪些业务被激活哪些业务没有开通

    EFACM累加计费计数器

    6F39

    ACM

    累计呼叫列表

    当前的呼叫和以前的呼叫的单位总和

    EFGID1分组识别1

    6F3E

    GID1

    1级分组识别文件

    包含特定的SIM-ME组合的标识符,可以识别一组特定的SIM卡

    EFGID2分组识别2

    6F3F

    GID2

    2级分组识别文件

    包含特定的SIM-ME组合的标识符,可以识别一组特定的SIM卡

    EFPUCT单位价格/货币表

    6F41

    PUCT

    呼叫单位的价格和货币表

    PUCT是与计费通知有关的信息,ME用这个信息结合EFACM,以用户选择的货币来计算呼叫费用

    EFCBMI小区广播识别号

    6F45

    CBMI

    小区广播信息标识符

    规定了用户希望MS采纳的小区广播消息内容的类型

    EFSPN服务提供商

    6F46

    SPN

    服务提供商名称

    包含服务提供商的名称和ME显示的相应要求

    EFCBMID

    6F48

    CBMID

    数据下载的小区广播消息识别符

    移动台将收到的CBMID传送给SIM卡

    EFSUME

    6F54

    SUME

    建立菜单单元

    建立SIM卡中的菜单

    EFBCCH广播信道

    6F74

    BCCH

    广播控制信道

    由于BCCH的存储,在选择小区时,MS可以缩小对BCCH载波的搜索范围

    EFACC访问控制级别

    6F78

    ACC

    访问控制级别

    SIM卡有15个级别,10个普通级别,5个高级级别

    EFFPLMN禁止网络号

    6F7B

    FPLMN

    禁用的PLMN

    禁止选择除中国移动以外的其他运营商,比如中国联通、中国卫通等

    EFLOCI位置信息

    6F7E

    LOCI

     位置信息

    存储临时移动用户识别符、位置区信息等内容

    EFAD管理数据

    6FAD

    AD

    管理数据

    包括关于不同类型SIM卡操作模式的信息。例如:常规模式(PLMN用户用于GSM网络操作),型号认证模式(允许ME在无线设备的认证期间的特殊应用);小区测试模式(在小区商用之前,进行小区测试),制造商特定模式(允许ME制造商在维护阶段进行特定的性能自动测试)

    EFPHASE阶段

    6FAE

    PHASE

    阶段标识

    标识SIM卡所处的阶段信息,比如是普通SIM卡还是STK卡等

    DFTELECOM

    7F10

    电信目录

     

     

    EFADN缩位拨号

    6F3A

    AND

    电话簿

    用于将电话记录存放在SIM卡中

    EFFDN固定拨号

    6F3B

    FDN

    固定拨号

    包括固定拨号(FDN)和/或补充业务控制字串(SSC),还包括相关网络/承载能力的识别符和扩展记录的识别符,以及有关的α识别符

    EFSMS短消息

    6F3C

    SMS

    短消息

    用于将短消息记录存放在SIM卡中

    EFCCP能力配置参数

    6F3D

    CCP

    能力配置参数

    包括所需要的网络和承载能力的参数,以及当采用一个缩位拨号号码,固定拨号号码,MSISDN、最后拨号号码、服务拨号号码或禁止拨号方式等,建立呼叫时相关的ME配置

    EFMSISDN电话号码

    6F40

    MSISDN

    移动基站国际综合业务网号

    存放用户的手机号

    EFSMSP短信息参数

    6F42

    SMSP

    短消息业务参数

    包括短信中心号码等信息

    EFSMSS短信息状态

    6F43

    SMSS

    短消息状态

    这个标识是用来控制流量的

    EFLND最后拨号

    6F44

    LND

    最后拨叫号码

    存储最后拨叫号码

    EFExt1扩展文件1

    6F4A

    EXT1

    扩展文件1

    包括AND,MSISDN或LND的扩展数据

    EFExt2扩展文件2

    6F4B

    EXT2

    扩展文件2

    包含FDN的扩展数据

    · 

     

    7、如何判断是否有SD卡?

     通过如下方法:

    Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

    如果返回true就是有sdcard,如果返回false则没有。

    8、dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念?

    dvm指dalvik的虚拟机。每一个Android应用程序都拥有一个独立的Dalvik虚拟机实例,应用程序都在它自己的进程中运行。而每一个dvm都是在Linux 中的一个进程,所以说可以近似认为是同一个概念。

    什么是android DVM:Dalvik是Google公司自己设计用于Android平台的Java虚拟机,每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

    Dalvik和Java虚拟机的区别

    1:Dalvik主要是完成对象生命周期管理,堆栈管理,线程管理,安全和异常管理,以及垃圾回收等等重要功能。   

    2:Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。

    3:不同于Java虚拟机运行java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex

    4: dex文件格式可以减少整体文件尺寸,提高I/O操作的类查找速度。 

    5: odex是为了在运行过程中进一步提高性能,对dex文件的进一步优化。   

    6:所有的Android应用的线程都对应一个Linux线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制

    7:有一个特殊的虚拟机进程Zygote,他是虚拟机实例的孵化器。它在系统启动的时候就会产生,它会完成虚拟机的初始化,库的加载,预制类库和初始化的操作。如果系统需要一个新的虚拟机实例,它会迅速复制自身,以最快的数据提供给系统。对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域。

    9、Android程序与Java程序的区别?

     Android程序用android sdk开发,java程序用javasdk开发.

    Android SDK引用了大部分的Java SDK,少数部分被Android SDK抛弃,比如说界面部分,java.awt  swing  package除了java.awt.font被引用外,其他都被抛弃,在Android平台开发中不能使用。android sdk 添加工具jar httpclient , pull  opengl 

    展开全文
  • 第一个 Java 程序

    千次阅读 多人点赞 2019-10-24 10:53:26
    第一个 Java 程序,从一简单的 Java 程序来开始学习 Java 语言。 Eclipse 创建并运行一 Java 程序 1、安装 Eclipse; 2、打开 Eclipse; 首先,选择一目录作为Eclipse的工作目录(工作空间),开始有一...

    先给出一个简单的 Java 程序,进入到 Java 语言的世界。

    第一个 Java 程序,从一个简单的 Java 程序来开始学习 Java 语言。


    Eclipse 创建并运行一个 Java 程序

    1、安装 Eclipse;

    2、打开 Eclipse;

    首先,选择一个目录作为Eclipse的工作目录(工作空间),开始有一个默认的目录。如果想要更换目录,可以点击"Browse..."按钮,在弹出的文件目录中选择。

    下面有一个"Use this as the default and do not ask again"的可选框。这个选框的意思是"将当前目录作为默认的工作目录,以后不再询问"。如果勾选了这个可选框,那么以后启动 Eclipse,则不会弹出这个界面。工作目录将固定。

    有一个收缩按钮"Recent Workspaces",可以查看最近使用的工作目录,也可以在这里切换到最近使用的工作目录。

    3、创建项目

    开启 Eclipse 之后,会进入到欢迎界面,如如下图所示,感兴趣的可以看看。

    这里,就忽略欢迎界面,点击靠近左上角的地方,"Welcome"字迹后面有一个小叉,点击一下,关闭欢迎界面。

    进入到如下的界面,

    这是 Eclipse EE 版本(SE版本差别不大)打开后的初始化界面。可以将这个界面分成6个区域。

    • 1号区域是大家熟悉的菜单栏;

    • 2号区域是常用的快捷功能按钮;

    • 3号区域是工程项目浏览窗口;

    • 4号窗口是源代码编辑区域;

    • 5号和6号窗口是一些输出信息的窗口。

    点击1号区域的菜单栏"File","New","Project",(tips:Java SE 版本会直接有"Java Project")

    也可以选中3号区域,鼠标右键,选择"New","Java Project",

    选择"Java Project",

    单击"Next",

    在"Project name"输入框中填写项目的名称,可以根据需要选择 JavaSE 的版本,其余保持默认即可;点击"Finish"按钮。

    现在再来看看界面,

    在"Project Explorer"视窗中多出了一个名为"JavaStudy"的项目,打开,有两个部分。其一是"JRE System Library[JavaSE-1.8]",这个是 JRE 的运行库;另外一个名为"src"的文件夹,这是存放源代码的地方。

    点击"src",右键鼠标,选择"package",在"name"输入框中填写包的名字,

    结果如下图所示,

    选中包"louisluo",鼠标右键,依次选择"New","Class",如下如所示,

    单击鼠标左键,会出现如下的界面,

    • 这个界面中"Source folder"和"Package"在刚才的步骤中已经处理好了,在这里就不需要修改;

    • 1号区域是必填项,填写要创建的类的名称;

    • 2号区域是类修饰符,默认是"public",可以选择。"public"表示公有访问的,"package"表示包访问,"abstract"表示抽象的,"final"表示不可变的;

    • 3号区域是指明继承的父类,默认是所有类的父类"java.lang.Object"类,可以点击右边的"Browse..."浏览查找父类;

    • 4号区域是指明实现的接口,默认是没有实现任何接口的,可以点击右边的"Add"进行搜索添加;

    • 5号区域是快捷的功能选择区,可以选择自动生成"main"方法,构造器,抽象方法和一些模板信息等。

    填写了类名之后,其余的保持默认即可。现在的界面如下图所示,

    在上面的红框中,照着下面的程序内容填写即可。

    以下是源程序:文件名称为 "HelloWorld.java"。

    // 双斜杠后面的内容都是注释,对源程序没有任何影响
    // 当前文件所处的包名
    package com.louis.www;
    
    // 类名
    public class HelloWorld
    {
    	// Java程序的入口方法,程序将从这里开始执行
    	public static void main(String[] args)
    	{
    		// 向控制台输出一条语句
    		System.out.println("Hello,Welcome to Java World!");
    	}
    }

    编译、运行上面的源程序,得到下面的输出结果。

    Eclipse 在敲入代码的时候,会实时的检查 Java 源代码的语法正确性。如果出现错误,会在错误的地方以红色标注,并且错误的那一行左边也会有标注,如下图所示,

    故意将"public"写错成"Public",Eclipse 立马就在错误的地方"Public"以红色标注,并且在左边行号"5"处进行标错有错误。将鼠标移动到"Public"上面或者左边红叉上面,就会有错误原因提示,以及修改意见。

    选中正在编写的 Java 源文件,保存。在编辑区域鼠标右键,选择"Run as",然后选择"Java Application",点击鼠标左键,就会输出结果。

    输出的结果如下:

    HelloWorld源程序的运行结果

     


    记事本等文本编辑器创建并运行一个 Java 程序

    首先在计算机任何一个地方创建一个文件夹,随意命名。本文在桌面创建一个名为"louis"的空文件夹。

    然后在文件夹"louis"中,新建一个记事本文件,重命名为"HelloWorld.java",会出现如下窗口,

    选择"是",就创建了一个 Java 源代码文件。

    用文本工具(notepad,sublimetext,EditPlus等等)打开,编写如下代码,保存。

    package Pursueluo;
    public class HelloWorld
    {
    	public static void main(String[] args)
    	{
    		System.out.println("Hello,Welcome to Java World!");
    	}
    }

    进入Windows的命令提示符(cmd),如下图所示,

    打开后的界面如下,

    本文的源文件放在桌面上的"louis"文件夹中,

    本文桌面的绝对路径是:"C:\Users\Administrator\Desktop";

    "louis"文件夹的绝对路径是:"C:\Users\Administrator\Desktop\louis"

    进入到"louis"目录,然后使用如下命令

    javac -d . ./HelloWorld.java

    再来看看Windows文件资源管理器中发生了什么。

    多了一个名为"Pursueluo"的文件夹(tips:这不就是源代码中的 "package Pursueluo;"包名吗?);

    打开"Pursueluo"文件夹,看看有什么东西。

    多了一个名称为"HelloWorld'的文件(tips:"HelloWorld"不就是源代码中的"class HelloWorld"类名吗?),后缀名为".class"文件,这就是 Java 中大名鼎鼎的字节码文件。

    好了,下面继续本文的工作。

    在控制台窗口中输入下面的命令,

    java Pursueluo.HelloWorld

    红色框中输出了结果,正确,完成。

    如果对编译和运行的命令,有什么疑问的,可以参看下面的文章。(也是搜的!!!)

    Windows下命令行编译一个简单的 Java 文件 (这个要简洁版,入门版)

    用命令行编译 java 文件(这个是复杂版,提升版)


    现在,我们来将这个源程序进行拆分,来了解其中的细节。

    首先Java是严格区分大小写的,所以类似于 package、public、class、static、void、main 等等,都是Java的关键字,必须要求小写。

     

    // 当前文件所处的包名
    package com.louis.www;

    这句话的作用是:指明当前文件所处的包。包是Java中的一种机制,为了解决同名文件的问题。关键字 package 后紧跟包名。语法规则是:package 包名;

    • 如果显式的指明包名,那么这句话必须放在文件的所有语句的第一行;否则,会报错。

    • 如果隐式的指明包名,那么这句话不用写。表示这个文件放到默认的包中(default 包)。

     

    // 类名
    public class HelloWorld
    {
        ……
    }

    这段代码定义了一个类,名字叫做 HelloWorld。

    关键字 class 是用于定义类,后面紧跟类名。关键字 class 前面可以添加修饰符。修饰符可以是 public(公有访问) 、default(默认访问) 、final(不可变) 、abstarct(抽象)。

    语法规则是:public | default [ final | abstact ] class 类名;

    其中,“public" 和 "default" 是必选其一的(default 就是不写访问修饰符);"final" 和 "abstract" 是可选项,二者不可同时出现。"class" 关键字和 "类名"不可省略。且类名不能是Java语法的关键字和保留字。

     

    public static void main(String[] args)
    {	
        ……
    }

    这是 main 方法的固定写法,也即是说,main 方法前面必须加上 public static void 修饰符,参数为 String 类型的一个数组。其中, public 表示这个 main 方法是公有访问的;static 表示这个 main 方法是静态的,是属于这个类的;void 表示这个main 方法是没有返回值的;参数 String[] args 是表示这个 main 方法接收字符串数组类型的参数,一般是命令行参数。

     

    System.out.println("Hello,Welcome to Java World!");

    这句话调用了Java 的库方法。

    System 是 Java 库中 lang 包下的一个系统类;

    out 是标准输出类(这里是指屏幕);

    println 是输出方法,它实现的功能是输出括号中的字符串,然后换行。

    所以最终的结果是在(屏幕)控制台上输出字符串 "Hello,Welcome to Java World!",然后换行。

    这里要注意的是,调用方法使用的是 "." 运算符。

     

    展开全文
  • 微信公众号:码农充电站pro ... 如果你发现特殊情况太多,那你肯定是用错方法了。 —— Carig Zerouni 当你在自己的电脑上安装好Python 后,就可以编写Python 程序了。...1,使用Python 交互式终端 ...

    微信公众号:码农充电站pro
    个人主页:https://codeshellme.github.io

    如果你发现特殊情况太多,那你肯定是用错方法了。
    —— Carig Zerouni

    当你在自己的电脑上安装好Python 后,就可以编写Python 程序了。

    你可以使用Python 交互式终端,也可以将代码写在文件中,然后用Python 解释器来运行代码。

    1,使用Python 交互式终端

    Python 解释器就是一个交互式终端,所谓交互式终端,就是你输入的代码,会被立即执行,并将结果反馈给你。

    直接运行Python 解释器就会进入交互模式:

    $ python3
    ____________________________________________
    Python 3.8.0 (default, Oct 28 2019, 16:14:01) 
    [GCC 8.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 100 + 200      # 计算 100 + 200
    300                # 计算结果
    >>> 
    >>> print('Hello Python.')  # 输出 "Hello Python."
    Hello Python.				# 输出结果
    >>>
    >>> exit()                  # 退出终端,quit() 亦可
    

    其中的符号>>> 后面可以输入Python 代码,按回车键 后,解释器会执行你输入的代码,并将执行结果显示出来。

    使用print() 函数可以输出字符串,字符串需要单引号双引号引住。

    使用exit()quit() 函数可以退出终端。

    2,将Python 代码写在文件中

    一般交互式终端比较适合验证/测试你的想法,其并不适合编写大段的代码,写在交互式终端内的代码也无法保存下来。

    一个完整的程序代码都需要写在文件中,那么一个顺手的代码编辑器便可起到事半功倍的效果。

    2.1,Python IDE 推荐

    如果你使用的是类Unix 系统,Vim 是个不错的代码编辑器,但Vim 对初学者并不是很友好,你可以选择一款喜欢的IDE。

    IDE 即集成开发工具,一般都具备编写代码,代码高亮,调试代码,运行代码等功能。

    这里推荐两款不错的Python IDE:

    2.2,编写Python 代码

    我们编写如下代码,文件名为hello.py

    print('Hello Python.')
    

    注意:Python 代码的文件名后缀为.py

    2.3,运行Python 代码

    使用Python 解释器,后跟文件名,可以运行Python 代码,如下:

    $ python3 hello.py `执行Python 代码`
    Hello Python.	   `输出字符串`
    

    3,Python 可执行文件

    一般在一个Python 代码文件的第一行会有一行特殊的代码#! /usr/bin/env python3,例如hello.py

    #! /usr/bin/env python3
    print('Hello Python.')
    

    仅仅这一行代码并没有什么意义,你需要对该文件添加可执行权限,如下:

    chmod +x hello.py
    

    此时hello.py 文件就是一个可执行的Python 脚本,file 命令可以查看一个文件的类型:

    $ file hello.py 
    hello.py: a /usr/bin/env python3 script, ASCII text executable
    

    这样就可以像如下方式执行Python 程序:

    $ ./hello.py 
    Hello Python.
    

    注意:

    1. #! /usr/bin/env python3 这行代码的实际作用是告诉系统,当使用./hello.py 来执行代码的时候,使用系统环境中的python3 来执行该文件。
    2. Python 可执行文件只对类Unix 系统有效,对Windows 系统无效。

    (完。)


    推荐阅读:

    Python 简明教程 — 0,前言
    Python 简明教程 — 1,搭建Python 环境


    欢迎关注作者公众号,获取更多技术干货。

    码农充电站pro

    展开全文
  • 输入格式:输入在行中给出一个正整数N(&lt;10)。输出格式:输出N×N的螺旋方阵。每行N数字,每数字占3位。输入样例:5 输出样例: 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 ...

    输入格式:

    输入在一行中给出一个正整数N<10)。

    输出格式:

    输出N×N的螺旋方阵。每行N个数字,每个数字占3位。

    输入样例:

    5
    

    输出样例:

      1  2  3  4  5
     16 17 18 19  6
     15 24 25 20  7
     14 23 22 21  8
     13 12 11 10  9
    #include <stdio.h>
    main()
    {
    	int c=1,x=0, y=0,n, a[10][10]={0};
    	scanf("%d", &n);
    	a[x][y] = 1;
    	while(n*n!=c)
    	{
    	    while(y+1<n && !a[x][y+1] )
    		     a[x][++y] = ++c;
    		while(x+1<n  && !a[x+1][y])
    		     a[++x][y] = ++c;
    		while(y-1>=0  && !a[x][y-1])
    		     a[x][--y] = ++c;
    		while(x-1>=0 && !a[x-1][y])
    		     a[--x][y] = ++c;
        }
    	for(x=0;x<n;x++)
    	{ 
    	   for(y=0;y<n;y++)
    	   	{
    	   		printf("%3d", a[x][y]);
    		}
    	   	printf("\n");
        }
     } 

    展开全文
  • "Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday" def whatDayIsToday(self, letter): """通过分析各个字母可知...如果是T或者是S,那么通过字母也可以直接判断出来了""" letter = str(let
  • 从Map中取出第一个 key 和 value 的方法,使用Map.Entry进行操作; 可与LinckedHashMap(按元素存入顺序排序的有序Hash表)搭配使用;实现特定的业务要求;/** * 获取map中第一个key值 * * @param map 数据源 * ...
  • 题目:C语言:有一个字符串,包含n字符。写一个函数,将此字符串中从m字符开始的全部字符复制成为另一个字符串
  • public class Main3 extends Thread{ int i,j,x=0; Main3(int m,int n){ this.i = m; this.j = n; } public void run() { int p,q; p=0;q=0; for(int m=i;... for(int h=1;h&lt;=...
  • 由于一周只有七天,每月的第一个周六肯定会落在每月的头七天里,所以可以这样:0 0 1-7 * 6 或者把要执行的命令写shell角本,在角本的开头判断一下,当前的日期是不是在1-7之间,如果在1-7之间再执行命令。...
  • // 猴子第一天摘下来若干桃,当即吃了一半,不过瘾, // 又多吃了一,第二天早上将剩下的桃子又吃了一半,又不过瘾,又多吃了一, // 以后每天早上都吃了前一天剩下的一半多一,到10第十天,见只剩下一...
  • 数组取第一个元素和最后一元素

    千次阅读 2018-11-07 10:19:54
    $array = array(1,2,3,4,5); //复位 reset($array); //第一个 $first = current($array); //最后一 $end = end($array);
  • 本题是一道来自《剑指offer》的题目:输入两链表,找出它们的第一个公共结点。 本题思路有两第一种暴力破解法循环嵌套查找,第二求差法。 循环嵌套查找 从第一个链表的头节点开始,在第二链表找相同的...
  • package LQB; public class B2016Yc1 { public static void main(String[] args) { int num = 0;... for(int i = 1;i&lt;=100;i++){ num+=i; sum+=num; } System.out.println(sum); } }  
  • Android 设置第一个Activity

    千次阅读 2017-04-25 11:44:58
    Android Studio 默认的第一个启动Activity是MainActivity,如果我们要修改第一个启动的Activity,该如何设置呢?需要在文件清单里面(AndroidManifest.xml)中做修改。默认MainActivity为第一个启动页面,代码如下: ...
  • PyCharm创建第一个项目

    万次阅读 2018-05-21 17:20:27
    PyCharm创建第一个项目 友链:python安装教程,pycharm安装教程 create new project 选择项目路径 选择好之后点击create 创建 提示,关闭即可 close 可以看到,第一个learnPython项目就创建成功了 ...
  • 这两天做统计,需要用到当月第一天0点0分0秒的unix timestamp,上第一天0点的unix时间戳,三月前月第一天的0点的Unix时间戳,六月前当月第一天的0点的Unix时间戳,现在整理如下,看代码和执行的结果,凑了...
  • 第一个错误的版本

    万次阅读 2018-12-11 14:41:59
    leetcode链接 你是产品经理,目前正在带领一...假设你有 n 版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 versi...
  • 缺失的第一个正数 文章目录Leetcode算法Java全解答--41. 缺失的第一个正数题目想法结果总结代码我的答案大佬们的答案测试用例其他 题目 给定一未排序的整数数组,找出其中没有出现的最小的正整数。 说明: 你的...
  • 第一个汇编程序

    千次阅读 2016-10-29 21:36:13
    王爽汇编四章笔记+amsm+link软件下载
  • 2016-2017学年二学期C++四章(1

    千次阅读 2017-04-30 20:54:43
    C++ 上机课参考答案 本系列文章供北京邮电大学信通院及数媒学院「C++高级程序语言设计」上机课学生参考。 C 上机课参考答案 2016-2017学年二学期C四章1 VC实验41 数组使用 ...编程:将1~100的自然数存到
1 2 3 4 5 ... 20
收藏数 11,294,221
精华内容 4,517,688
关键字:

第一个