The main difference between Slice and Page is the latter provides non-trivial pagination details such as total number of records(getTotalElements()), total number of pages(getTotalPages()), and next-page availability status(hasNext()) that satisfies the query conditions, on the other hand, the former only provides pagination details such as next-page availability status(hasNext()) compared to its counterpart Page. Slice gives significant performance benefits when you deal with a colossal table with burgeoning records.
Let's dig deeper into its technical implementation of both variants.
static class PagedExecution extends JpaQueryExecution {
    @Override
    protected Object doExecute(final AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {
        Query query = repositoryQuery.createQuery(accessor);
        return PageableExecutionUtils.getPage(query.getResultList(), accessor.getPageable(),
                () -> count(repositoryQuery, accessor));
    }
    private long count(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {
        List<?> totals = repositoryQuery.createCountQuery(accessor).getResultList();
        return (totals.size() == 1 ? CONVERSION_SERVICE.convert(totals.get(0), Long.class) : totals.size());
    }
}
If you observe the above code snippet, PagedExecution#doExecute method underlyingly calls PagedExecution#count method to get the total number of records satisfying the condition.
    static class SlicedExecution extends JpaQueryExecution {
    @Override
    protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) {
        Pageable pageable = accessor.getPageable();
        Query createQuery = query.createQuery(accessor);
        int pageSize = 0;
        if (pageable.isPaged()) {
            pageSize = pageable.getPageSize();
            createQuery.setMaxResults(pageSize + 1);
        }
        List<Object> resultList = createQuery.getResultList();
        boolean hasNext = pageable.isPaged() && resultList.size() > pageSize;
        return new SliceImpl<>(hasNext ? resultList.subList(0, pageSize) : resultList, pageable, hasNext);
    }
}
If you observe the above code snippet, to findout whether next set of results present or not (for hasNext()) the SlicedExecution#doExecute method always fetch extra one element(createQuery.setMaxResults(pageSize + 1)) and skip it based on the pageSize condition(hasNext ? resultList.subList(0, pageSize) : resultList).
- Application:
- Page - 
- Use when UI/GUI expects to displays all the results at the initial stage of the search/query itself, with page numbers to traverse(ex., bankStatement with pagenumbers) 
 
- Slice - 
- Use when UI/GUI expects to doesnot interested to show all the results at the initial stage of the search/query itself, but intent to show the records to traverse based on scrolling or next button click event (ex., facebook feed search)